HDU 5823 color 状压DP

来源:互联网 发布:char占几个字节 java 编辑:程序博客网 时间:2024/05/18 00:42

时空隧道


好久木有写过blog了….
感觉每次写DP的题解访问量都是最多的…..


题目大意:
你有⼀个 n 个点的⽆向图,点的标号从 0 到 n − 1。
众所周知,n 个点的集合有 2 n − 1 个⾮空⼦集。
对于每⼀个⾮空⼦集 S,定义 S 的合法染⾊是对 S 内的每个点染⼀种
颜⾊,使得 S 中不存在两个同⾊点间有边相连。
定义⼀个集合 S 的染⾊数 k 是 S 集合最⼩的合法染⾊的颜⾊数。
你的任务是求 n 个点的每⼀个⾮空⼦集的染⾊数。
(盗用zrt的翻译….>_<…..)


分析:
看到n<=18我那个开心….这不是妥妥的状压DP吗…再看一下问题….很激动呀,要求求出每个子集的ans也就是要遍历所有状态….这绝对是状压DP没错了….
然后一激动就GG了….我居然只枚举了比i少一个点的子集……(脑残的孩子只有慢慢来…)
状态转移其实很简单,f[i]=min(f[i],f[i^j]+1),j是i的子集并且j是一个独立集(就是说这些点没有边相连)大概就是说这些点都可以用一种颜色搞定….
首先遍历所有状态是2^n,枚举所有子集可是3^n(一个点有3种状态,属于当前集合但不属于子集,不属于当前集合,属于当前集合并且属于子集),有没有简单一点的枚举方式避免枚举无用状态?当然有了….
for(int j=i;j>0;j=(j-1)&i)
这很明显j是i的子集…..避免判断当前点集是否为i的子集….这样总复杂度为3^n….(j是否为独立集可以预处理的…..)
zrt的处理方法很机智:
因为这道题给边方式是邻接矩阵,所以就可以O(n)的判断了
感觉这道题收获还是很多的…..
还有一点很重要就是千万不要忘了位运算,位运算可以优化很多东西….(又想起了hhd出的回家种田的”水题”…..)


代码如下:

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>//by NeighThornusing namespace std;const int maxn=18+5;int n,M[maxn],independent[1<<18];unsigned int f[1<<18];char str[maxn];inline unsigned int power(unsigned int a,int b){    unsigned int ans=1;    while(b){        if(b&1)            ans=ans*a;        a=a*a,b>>=1;    }    return ans;}signed main(void){//  freopen("color.in","r",stdin);//  freopen("color.out","w",stdout);    int cas;    scanf("%d",&cas);    while(cas--){        scanf("%d",&n);        for(int i=0;i<n;i++){            scanf("%s",str);            M[i]=0;            for(int j=0;j<n;j++)                if(str[j]=='1')                    M[i]|=1<<j;        }        for(int i=1;i<1<<n;i++){            independent[i]=1;            for(int j=0;j<n;j++)                if((i>>j)&1)                    if(i&M[j]){                        independent[i]=0;                        break;                    }        }        f[0]=0;        for(int i=1;i<1<<n;i++){            f[i]=n;            for(int j=i;j>0;j=(j-1)&i)//怎么枚举子集很重要...                 if(independent[j])                    f[i]=min(f[i],f[i^j]+1);        }    //  for(int i=1;i<1<<n;i++)    //      cout<<i<<" "<<f[i]<<endl;        unsigned int ans=0;        for(int i=1;i<1<<n;i++)            ans+=f[i]*power((long long)233,i);        cout<<ans<<endl;    }    return 0;   }

by >_< NeighThorn

1 0
原创粉丝点击