题意
要把一些珠子串成一串,每个珠子只可以和限制特定的别的珠子串在一起,给定珠子数量和限制条件,求能组成的不同的项链的种数。
思路
看数据范围,再加上这是有限制条件的问题求解,很容易发现是状压dp的问题。dp[i][j]表示放下第i个珠子后的状态为j(即i为前一个珠子),只要保证下一个珠子能和前一个接的上就可行。那么枚举k为下一个珠子,看是否可行即可,具体看代码。然后细节上,点的下标与dp中的状态要统一起来才不会出错。
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 long long dp[20][1<<18]; 4 int G[20][20];//数据范围小,只需要判断是否邻接,所以矩阵很方便 5 int main() 6 { 7 int n,m; 8 while(~scanf("%d%d",&n,&m)) 9 { 10 memset(dp,0,sizeof(dp)); 11 memset(G,0,sizeof(G)); 12 for(int i=0,a,b;i<m;i++) 13 { 14 scanf("%d%d",&a,&b); 15 G[a][b]=G[b][a]=1;//这里没统一下标,所以后面写的时候出现了很多麻烦 16 } 17 dp[0][1]=1;//因为是个环,所以任意选取一颗珠子作为开头,此处选1 18 for(int i=1;i<(1<<n);i++) 19 { 20 for(int j=0;j<n;j++) 21 { 22 if(dp[j][i]==0) continue;//如果此状态不可达,跳过 23 for(int k=0;k<n;k++) 24 { 25 if(i&(1<<k)) continue;//如果这个珠子已经放了,跳过 26 if(G[k+1][j+1]) dp[k][i|(1<<k)]+=dp[j][i];//如果k与j(前一个珠子)能接上,就可以状态转移 27 } 28 } 29 } 30 long long ans=0; 31 for(int i=0;i<=n;i++) 32 { 33 if(G[1][i+1])//看最后一颗珠子与第一颗是否连的起来 34 ans+=dp[i][(1<<n)-1]; 35 } 36 printf("%lld ",ans); 37 } 38 }
后记
这题一开始用了三维dp,多了一维表示第一个珠子,然后最后再除阶乘得出答案,然而事实上由于是个环,所以从哪个珠子开始关系并不大。从这题上还是学到了很多的。