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 }

 

posted @ 2021-03-26 16:28  acmloser  阅读(69)  评论(0编辑  收藏  举报