A Simple Task CodeForces - 11D
考察:状压dp
完全没想到状压dp,思路全跑dfs了,但是dfs又觉得m范围不定数据可能很大....然后被卡死了....
题解参考了这位po主,写得比本蒟蒻好 GO
思路:
参考状压dp模板哈密顿路径f[i][j]表示到达i状态且最后落脚点为j的方案数,f[i|1<<j][j] += f[i][k]为倒数第二个点为k,当i的第j位已经为1时,就是一个环.
但是!
如果纯粹像上面的转移方程计算,会计重很多,并且会计算到重边.因此上面的转移方程只能是i>>j&1==0时才能这么计数,那么当i>>j&1用一个变量ans计数环.
但是!
一个环上有多个顶点,如果直接ans计数会计重,所以我们要以一个标准将环分类.我们假设p为当前环的起点,当我们只允许p只能往比它大的点出发时,就能去一部分重.但是这仍然有重复,比如下面: 假设A为起点,我们发现A->B->C和A->C->B会重复计算两次.一个环上起点只能往两边走,所以答案会重复计算两次.我们最后计算的ans-m(len==2的环个数)>>1就是答案.
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 using namespace std; 5 const int N = 19; 6 typedef long long LL; 7 bool g[N][N]; 8 int n,m; 9 LL f[1<<N][N]; 10 int lowbit(int x) 11 { 12 return x&-x; 13 } 14 LL solve() 15 { 16 LL ans = 0; 17 for(int i=0;i<n;i++) f[1<<i][i] = 1; 18 for(int i=1;i<1<<n;i++) 19 for(int j=0;j<n;j++) 20 if(f[i][j]) 21 for(int k=0;k<n;k++) 22 if(lowbit(i)<=(1<<k))//优化枚举起点的循环 23 if(g[j][k]) 24 { 25 if(!(i>>k&1))f[i|(1<<k)][k]+=f[i][j]; 26 else if(lowbit(i)==(1<<k))ans+=f[i][j]; 27 }//起点与终点相同 28 return ans; 29 } 30 int main() 31 { 32 scanf("%d%d",&n,&m); 33 for(int i=1;i<=m;i++) 34 { 35 int a,b; scanf("%d%d",&a,&b); 36 g[a-1][b-1] = g[b-1][a-1] = 1; 37 } 38 printf("%lld\n",solve()-m>>1); 39 return 0; 40 }