cf298F:状压dp+剪枝
div2的F题,只想到了一个复杂度略高的dp,T了几次,后来加了剪枝减掉一些无用的状态终于过了。。
题意:
一个n*m的矩阵 (n<=5,m<=20),对格子进行黑白染色,已经给出了每行每列黑色联通块的个数,要求输出一组答案,满足有解。
思路:
首先发现n只有5,考虑按列处理,每列总共有2^5=32种状态;又发现对于给出的联通块个数,最坏情况是当联通块个数等于1或者2的时候有15种情况
比直接二进制处理快了一倍。所以我们可以预先处理出联通块个数为0~3时对于的二进制状态;
找到了列的基本状态,现在来考虑转移:
首先在转移时应该满足每行的黑色联通块个数,所以每行当前的联通块个数是需要保存的,由于m<=20所以每行最多有0~10这11种情况,考虑到5行就总共有11^5种状态
所以总共的状态数即为 20*15*11^5=48315300 由于cf的机器很强大这个状态数基本算是可以接受了
转移时处理如下:对于每一行如果上一列为0且当前列为1,则联通块数目+1,其他情况联通块数目不变。
这样我们就可以解决整个dp的过程了,由于题目要求输出染色方案,我们就需要记录每一个状态的前驱,最后通过前驱获得答案
但是直接交上去还是会T的,这里有两个剪枝可以使用
1.在转移过程中如果当前行的联通块个数已经大于题目给的个数,直接把这个状态减掉
2.在转移过程中如果当前行的联通块个数在剩下的列里怎么取(最好情况为一黑一白这样染)都不可能达到题目给的个数,把这个状态减掉
最后终于ac了!
代码:
1 #include <iostream> 2 #include <stdio.h> 3 #include<string.h> 4 #include<algorithm> 5 #include<string> 6 #include<ctype.h> 7 #include<vector> 8 using namespace std; 9 #define MAXN 10000 10 bool dp[20][16][161060]; 11 int pret[49000000]; 12 int x[6]; 13 int y[21]; 14 int p[7]; 15 int ans[21]; 16 int v[21][21]; 17 int nn[21]; 18 int n,m; 19 int fun(int s) 20 { 21 int res=0; 22 int pre=0; 23 for(int i=0; i<n; i++) 24 { 25 if((s&(1<<i))&&pre==0) 26 { 27 res++; 28 } 29 pre=(bool)(s&(1<<i)); 30 } 31 return res; 32 } 33 inline int get(int s,int i) 34 { 35 return (s%p[i+1])/p[i]; 36 } 37 int fuck(int s,int pre,int now,int pos) 38 { 39 int res=0; 40 for(int i=0; i<n; i++) 41 { 42 int tmp=(get(s,i)+((!(pre&(1<<i)))&&(now&(1<<i)))); 43 if(tmp>x[i]) 44 return -1; 45 if(tmp+(m-pos)/2<x[i]) 46 return -1; 47 res+=tmp*p[i]; 48 } 49 return res; 50 } 51 inline int make(int i,int j,int s) 52 { 53 return s+j*161051+i*15*161051; 54 } 55 56 inline int getj(int t) 57 { 58 return (t%(15*161051))/161051; 59 } 60 61 char s[30][30]; 62 int main() 63 { 64 cin>>n>>m; 65 p[0]=1; 66 for(int i=1; i<=6; i++) 67 { 68 p[i]=p[i-1]*11; 69 } 70 for(int i=0; i<(1<<n); i++) 71 { 72 int tmp=fun(i); 73 v[tmp][nn[tmp]++]=i; 74 } 75 for(int i=0; i<n; i++) 76 { 77 cin>>x[i]; 78 } 79 for(int i=0; i<m; i++) 80 { 81 cin>>y[i]; 82 } 83 for(int i=0; i<nn[y[0]]; i++) 84 { 85 dp[0][i][fuck(0,0,v[y[0]][i],0)]=1; 86 } 87 int now,pre,st; 88 for(int i=1; i<m; i++) 89 { 90 for(int j=0; j<nn[y[i-1]]; j++) 91 { 92 for(int s=0; s<161051; s++) 93 { 94 if(dp[i-1][j][s]) 95 { 96 for(int k=0; k<nn[y[i]]; k++) 97 { 98 st=fuck(s,v[y[i-1]][j],v[y[i]][k],i); 99 if(st<0) 100 continue; 101 dp[i][k][st]=1; 102 pret[make(i,k,st)]=make(i-1,j,s); 103 } 104 } 105 } 106 } 107 } 108 now=0; 109 for(int i=0;i<n;i++) 110 { 111 now+=x[i]*p[i]; 112 } 113 int j=-1; 114 for(int i=0;i<nn[y[m-1]];i++) 115 { 116 if(dp[m-1][i][now]) 117 { 118 j=i; 119 break; 120 } 121 } 122 123 now=make(m-1,j,now); 124 for(int i=m-1;i>=0;i--) 125 { 126 ans[i]=v[y[i]][getj(now)]; 127 now=pret[now]; 128 } 129 for(int i=0;i<n;i++) 130 { 131 for(int j=0;j<m;j++) 132 { 133 s[i][j]=((ans[j])&(1<<i))?'*':'.'; 134 } 135 } 136 for(int i=0;i<n;i++) 137 { 138 puts(s[i]); 139 } 140 return 0; 141 }