Codeforces 11D A Simple Task 题解 [ 蓝 ] [ 状压 dp ]

思路不难想,细节比较多。

思路

观察到 \(n \le 19\) ,首先想到状压 dp 。

于是自然地定义 \(dp[j][i]\) 为:抵达点的状态为 \(i\) ,且此时在点 \(j\) 时,简单路径的条数。注意这里是简单路径的条数,而不是环的个数,因为环的个数要在 dp 过程中统计。这里的 dp 作用就在于求简单路径条数

在转移的时候,我们先定下当前状态 \(i\) ,然后选定当前处于哪个点 \(j\) ,最后决定去到哪个点 \(k\) 。与一般的吃奶酪模型不太一样,\(k\) 可以不在 \(i\) 中。

对于 \(k\)\(i\) 中且 \(k\) 为起点的情况,此时重复抵达一个点,这时候我们找到了环,则 \(ans+=dp[j][i]\)

对于 \(k\) 不在 \(i\) 中的情况,就要去到 \(k\) 那里,则 \(dp[k][i|(1<<k)]+=dp[j][i]\)

因为起点不同,经过的边相同的环视为同一个环,所以我们假定起点为当前状态的 lowbit ,注意在枚举 \(k\) 时我们不能让 \((1<<k)<lowbit(i)\) ,因为如果这样那么我们的起点就改变了,会统计到重复的。

另外,因为我们会把所有的无向边统计进去,所以我们的 \(ans-m\) 。又因为一条环会正着走一遍,反着走一遍,所以要把 \((ans-m)/2\) ,即为最终结果。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
bool g[55][55];
ll dp[20][600000],ans=0;
int lowbit(int x){return x&-x;}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		cin>>u>>v;
		u--,v--;
		g[u][v]=g[v][u]=1;
	}
	for(int i=0;i<n;i++)dp[i][1<<i]=1;
	for(int i=0;i<(1<<n);i++)
	{
		for(int j=0;j<n;j++)
		{
			if(((i>>j)&1)==0)continue;
			for(int k=0;k<n;k++)
			{
				if(!g[j][k]||j==k)continue;
				if(lowbit(i)>(1<<k))continue;// 一定要加特判,防止起点变化
				if((i>>k)&1)
				{
					if(lowbit(i)==(1<<k))ans+=dp[j][i];
				}
				else dp[k][i|(1<<k)]+=dp[j][i];
			}
		}
	}
	cout<<(ans-m)/2;
	return 0;
}
posted @ 2024-07-31 23:09  KS_Fszha  阅读(42)  评论(0)    收藏  举报