【计蒜客习题】蒜头君的蜡笔
懒得打字,直接截图了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 }