把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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);
}
posted @ 2018-08-09 14:34  Starlight_Glimmer  阅读(9)  评论(0编辑  收藏  举报  来源
浏览器标题切换
浏览器标题切换end