标签:
题意:
N个点构成的无向图,M条边描述这个无向图。
问这个无向图中共有多少个环。
(1 ≤ n ≤ 19, 0 ≤ m)
思路:
例子:
4 6
1 2
1 3
1 4
2 3
2 4
3 4
答案:7
画个图发现,直接暴力DFS有太多的重复计算。用DP。
枚举点数(状态),每个状态的起点、终点(起点可以不用枚举,因为反正是一个环,谁作为起点都一样)。
dp[S][i]:状态是S,i是终点 含义:从S中的第一个数s出发到达第i个点的方案数。如果s和i相加,总方案数ans+=dp[S][i]
dp[S][i]=sigma(dp[S‘][i‘]) S‘是去掉第i个点后的点集,i‘属于S‘且i‘和i相连。
代码:
int n,m; char G[25][25]; int S[1<<19]; //全局状态 int cnS; int P[25]; //全局指针 int cnP; ll dp[(1<<19)+5][25]; ll ans; void finds(int nn,int fNum,int nowNum,int nowPos,int Ss){ //长度为nn,共要放fNum个,现在已放nowNum个,现在正在nowPos位置要进行第nowNum+1个放置的尝试 if(nowNum==fNum){ S[++cnS]=Ss; return; } int t1=fNum-nowNum; rep(i,nowPos,nn-t1+1){ int nSs=Ss+(1<<(i-1)); finds(nn,fNum,nowNum+1,i+1,nSs); } } void calc(int nn,int S){ //总长度为nn,计算状态S哪些位置上为1,存在全局指针P【】中。 cnP=0; rep(i,1,nn){ int t=(1<<(i-1)); if((S&t)>0){ P[++cnP]=i; } } } void init(){ cnS=0; cnP=0; finds(n,2,0,1,0); mem(dp,0); rep(i,1,cnS){ int ss=S[i]; calc(n,ss); rep(j,2,cnP){ if(G[P[1]][P[j]]==1){ dp[ss][P[j]]=1; } } } } void solve(){ rep(i,3,n){ //状态由i个点构成 cnS=0; cnP=0; finds(n,i,0,1,0); rep(tt,1,cnS){ int ss=S[tt]; calc(n,ss); rep(j,2,cnP){ int pj=P[j]; //终点 int nss=ss-(1<<(pj-1)); //上一个状态 rep(k,2,cnP){ //枚举终点 if(k==j) continue; if(G[pj][P[k]]==0) continue; int pk=P[k]; dp[ss][pj]+=dp[nss][pk]; } if(G[P[1]][pj]==1){ ans+=dp[ss][pj]; } } } } } int main(){ cin>>n>>m; mem(G,0); while(m--){ int a,b; scanf("%d%d",&a,&b); G[a][b]=G[b][a]=1; } if(n==1 || n==2){ puts("0"); } else{ init(); ans=0; solve(); printf("%I64d\n",ans/2); } return 0; }
标签:
原文地址:http://www.cnblogs.com/fish7/p/4309751.html