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

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

思路

观察到 n19 ,首先想到状压 dp 。

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

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

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

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

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

另外,因为我们会把所有的无向边统计进去,所以我们的 ansm 。又因为一条环会正着走一遍,反着走一遍,所以要把 (ansm)/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 @   KS_Fszha  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示