【计蒜客习题】蒜头君的蜡笔

懒得打字,直接截图了QwQ。。。


 

呃呃,状压DP和图论结合以后,思维难度确实上升了,但本质却没有发生改变。

我们可以定义dp[i]表示i这种状态最小需要的颜色数,那么我们可以预处理出只需一种颜色就可以染完的状态,然后枚举i的子集j,有dp[i]=min{dp[j]+dp[i-j]}。一种颜色就能染完,需要状态选中的各个点之间没有边相连。

其实这些还好说,关键最后统计答案的时候!!!

这里补充个小技巧,如果题目要求对2^32取模,可以将存储答案的变量设为unsigned类型,让其自然溢出;如果是对2^64取模,可以设成unsigned long long。

我一开始用的十分原始的long long,然后对2^32取模,,,但是2^32*2^32会炸long long!猝不及防的WA。

 1 #include<cstdio>
 2 #include<cstring>
 3 inline int min(int a,int b) {return a<b?a:b;}
 4 void put_num(long long i) {
 5     if(i>9) put_num(i/10);
 6     putchar(i%10+'0');
 7 }
 8 const int maxn=20,inf=0x3f3f3f3f;
 9 const long long mod=4294967296;
10 int n,point[maxn],G[maxn][maxn],dp[1<<maxn];
11 unsigned ans;
12 inline long long pow(int a,int b) {
13     long long c=1,x=a;
14     while(b) {
15         if(b&1) c*=x,c%=mod;
16         x=(x*x)%mod,b>>=1;
17     }
18     return c;
19 }
20 int main() {
21     scanf("%d",&n);
22     for(int i=1;i<=n;++i) {
23         char c;
24         for(int j=1;j<=n;++j) {
25             while((c=getchar())=='\n'||c==' '||c=='\r');
26             if(c=='1') G[i][j]=1;
27         }
28     }
29     memset(dp,inf,sizeof(dp));
30     dp[0]=0;
31     for(int i=1;i<(1<<n);++i) {
32         int flag=1;
33         for(int j=0;j<n;++j) if((1<<j)&i)
34             for(int k=0;k<n;++k) if(((1<<k)&i)&&j!=k)
35                 if(!flag||G[j+1][k+1]) {flag=0;break;}
36         if(flag) dp[i]=1;
37     }
38     for(int i=1;i<(1<<n);++i) {
39         for(int k=i;k;k=(k-1)&i)
40             dp[i]=min(dp[i],dp[k]+dp[i-k]);
41         ans+=dp[i]*pow(233,i)%mod;
42     }
43     put_num(ans);
44     return 0;
45 }
AC代码

 

posted @ 2018-09-13 19:43  Mr^Kevin  阅读(296)  评论(0编辑  收藏  举报