A Simple Task CodeForces - 11D【状压dp】
参考博客:
CodeForces - 11D A Simple Task
好久没写状压dp了,对于集合状态的表示都有些生疏:
空集.............................0
只含有第i个元素的集合{i}............1<<i
含有全部n个元素的集合{0,1...n-1}....(1<<n)-1//含有n个元素的全集
判断第i个元素是否属于集合S...........if(S>>i&1)
向集合中加入第i个元素S∪{i}..........s|1<<i
从集合中取出第i个元素...............s&~(1<<i)
集合S和T的并集S∪T.................S|T
集合S和T的交集S∩T.................S&T
推荐配合一起食用:集合的二进制数表示
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 20
#define INF 0x3f3f3f3f
#define LL long long
int n,m;
LL dp[1<<MAXN][MAXN];//路径条数会爆int
int g[MAXN][MAXN];//数据范围很小,用邻接矩阵可以方便地判断2点是否联通
int st(int s)//求点集中最右边的1,即节点编号最小的点
{
int ret=1;
while(s)
{
if(s%2)
return ret;
s>>=1,ret++;
}
}
int main()
{
int u,v;
LL ans=0;
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d %d",&u,&v);
g[u][v]=g[v][u]=1;
}
int S=(1<<n)-1;//含有n个元素的全集
for(int i=1;i<=n;i++)
dp[(1<<(i-1))][i]=1;//每个点到自己的路径条数为1
for(int s=1;s<=S;s++)
for(int u=1;u<=n;u++)
{
if(dp[s][u]==0) continue;
int fir=st(s);//求起点
for(int v=fir;v<=n;v++)
if(u!=v&&g[u][v])
{
if((s&(1<<(v-1)))&&v==fir)
ans+=dp[s][u];
else if( !(s&(1<<(v-1))))
{
int ss=s|(1<<(v-1));
dp[ss][v]+=dp[s][u];
}
}
}
ans-=m;//减去互相连边的情况 1->2->1
ans/=2;//每种都会算两次 1->2->4->3->1=1->3->4->2->1
printf("%lld\n",ans);
}
转载请注明出处,有疑问欢迎探讨
博主邮箱 2775182058@qq.com