UVA 10572 Black & White (状压DP)
题意:有一个n*m的矩阵,其中部分格子已经涂黑,部分涂白,要求为其他格子也上黑/白色,问有多少种涂法可以满足一下要求:
(1)任意2*2的子矩阵不可以同色。
(2)所有格子必须上色。
(3)只能有两个连通分量(即1黑1白)。
注:1<n,m<9。若能满足,顺便输出任一种涂色方法。
思路:
本来题也不难,只是刚开始写最小表示法,加上这题的难度,所以搞非常久。注:本题用最小表示法比较好。
大概逻辑如下:
if g[i][j]非首列且2*2子矩阵同色 then 非法状态;
if 上格连通分量不会丢失. then 合格状态;
然后对矩阵最后的两个格子特判如下(合法状态指的是对当前格子合法):
1)倒数第2格子的判断:
(1)if g[i][j]与上格同色 then 合法状态;
(2) if 上格的连通分量编号在轮廓线上并不是唯一 then 合法状态;
(3) if 轮廓线上所有格子颜色相同 then 合法状态;
2)倒数第1格子的判断:
(1)if 先尝试上面3种情况,若合法,再继续。
(2)if n=2 or 最后一个格子与上/左格(任一个以上都行)连通 then 合法状态;
当扫完所有格子后,将所有合法中连通分量最多为2个的都是符合要求的。路径记录只需要用一个64位整型来记录即可,较简单。
1 #include <bits/stdc++.h> 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 #define pii pair<int,int> 6 #define INF 0x3f3f3f3f 7 #define LL long long 8 #define ULL unsigned long long 9 using namespace std; 10 const int N=10; 11 int g[N][N], code[N],color[N], cur, n, m; 12 int num[N]; //用于最小表示法 13 char str[N][N]; //用于路径输出 14 struct Hash_Map 15 { 16 static const int mod=12357; 17 static const int NN=20010; 18 int head[mod]; //桶指针 19 int next[NN]; //记录链的信息 20 LL status[NN]; //状态 21 LL value[NN]; //状态对应的DP值 22 ULL path[NN]; //路径的状态 23 int size; 24 25 void clear() 26 { 27 memset(head, -1, sizeof(head)); 28 size = 0; 29 } 30 31 void insert(LL st, LL val,ULL p) 32 { 33 int h = st%mod; 34 for(int i=head[h]; i!=-1; i=next[i]) 35 { 36 if(status[i] == st) 37 { 38 value[i] += val; 39 path[i] = p; //任一路径 40 return ; 41 } 42 } 43 status[size]= st; 44 value[size] = val; 45 path[size] = p; 46 next[size] = head[h] ; 47 head[h] = size++; 48 } 49 }hashmap[2]; 50 51 LL encode() //编码 52 { 53 memset(num,-1,sizeof(num)); 54 LL s=0; 55 for(int i=m-1,cnt=0; i>=0; i--) 56 { 57 if(num[code[i]]<0) num[code[i]]=cnt++; 58 s<<=4; 59 s+=(num[code[i]]&7)+color[i]*8; //最高位为颜色,低3位为轮廓线 60 } 61 s<<=1; 62 s+=color[m]; //color[m]放在最低位,方便处理。 63 return s; 64 } 65 66 void decode(LL s) //解码:1个颜色位+3个状态位表示一个格子状态 67 { 68 color[m+1]=(s&1);s>>=1; 69 for(int i=1; i<=m; i++) //解码到code[1~m]上面,就不用移动。 70 { 71 code[i]=(s&7); s>>=3; 72 color[i]=(s&1); s>>=1; 73 } 74 } 75 76 inline bool isunique() //判断上格子的连通分量是否唯一。 77 { 78 for(int i=1; i<m; i++) if( code[i]==code[m] ) return false; 79 return true; 80 } 81 inline bool samecolor() //轮廓线上除了color[m]外,所有格子是否同色 82 { 83 for(int i=1; i<m; i++) if(color[i]!=color[i-1]) return false; 84 return true; 85 } 86 87 void comb(int c,int j) //合并连通分量 88 { 89 if( c==color[m] ) code[0]=code[m]; //与上同色 90 else code[0]=7; 91 if(j>0 && c==color[1] ) //与左同色 92 { 93 int t=code[0]; 94 for(int i=0; i<m; i++) if( code[i]==t ) code[i]=code[1]; 95 } 96 } 97 98 void trycover(int c,LL v,ULL p,int i,int j) //尝试对g[i,j]涂c色 99 { 100 if( j>0 && color[1]==c && color[m+1]==c && color[m]==c ) return ; //2*2同色 101 color[0]=c&1; 102 if(i+1==n && j+2==m) //最后2格特处理 103 { 104 if( c==color[m] || !isunique() || samecolor() ) 105 { 106 comb(c,j); 107 hashmap[cur].insert(encode(), v, p); 108 } 109 } 110 else if(i+1==n && j+1==m)//最后1格特处理 111 { 112 if( c==color[m] || !isunique() || samecolor() ) 113 { 114 if(i<2|| c==color[m]||c==color[1]) //如果是m=2的矩阵,需要特别注意最后两个格子。 115 { 116 comb(c,j); 117 hashmap[cur].insert(encode(), v, p); 118 } 119 } 120 } 121 else 122 { 123 if( c!=color[m] && isunique() ) return ; //连通分量消失 124 comb(c,j); 125 hashmap[cur].insert(encode(), v, p); 126 } 127 } 128 129 void cal() 130 { 131 hashmap[cur=0].clear(); 132 for(int i=0; i<(1<<m); i++ ) //产生第1行的所有情况。 133 { 134 color[m]=0; 135 bool flag=1; 136 for(int j=0,t=i; j<m; j++,t>>=1) //判断是否冲突。 137 { 138 color[j]=(t&1); 139 if(g[0][m-1-j]<2 && g[0][m-1-j]!=color[j]) flag=0; //注意点 140 } 141 if(flag) 142 { 143 code[m-1]=0; 144 for(int j=m-2,up=0; j>=0; j--) 145 if(color[j]==color[j+1]) code[j]=code[j+1]; 146 else code[j]=++up; 147 hashmap[0].insert(encode(), 1, i); 148 } 149 } 150 151 for(int i=1; i<n; i++) //穷举剩下所有格子。 152 { 153 for(int j=0; j<m; j++) 154 { 155 cur^=1; 156 hashmap[cur].clear(); 157 for(int k=0; k<hashmap[cur^1].size; k++) 158 { 159 LL s=hashmap[cur^1].status[k]; 160 LL v=hashmap[cur^1].value[k]; 161 ULL p=hashmap[cur^1].path[k]; 162 decode(s); 163 p<<=1; 164 if( g[i][j]==2 ) 165 { 166 trycover(0,v,p+0,i,j); 167 trycover(1,v,p+1,i,j); 168 } 169 else trycover(g[i][j],v,p+g[i][j],i,j); 170 } 171 } 172 } 173 } 174 175 void print() //答案及路径输出。 176 { 177 int ans=0;ULL mapp=0; 178 for(int i=0; i<hashmap[cur].size; i++) 179 { 180 int big=0; 181 decode(hashmap[cur].status[i]); 182 for(int y=1; y<=m; y++) big=max(big, code[y]); //求最大编号 183 if(big<=1) 184 { 185 mapp=hashmap[cur].path[i]; 186 ans+=hashmap[cur].value[i]; 187 } 188 } 189 printf("%d\n", ans); 190 if(ans) 191 { 192 memset(str,'\0',sizeof(str)); 193 for(int i=n-1; i>=0; i--) 194 { 195 for(int j=m-1; j>=0; j--) 196 { 197 if(mapp&1) str[i][j]='#'; 198 else str[i][j]='o'; 199 mapp>>=1; 200 } 201 } 202 for(int i=0; i<n; i++) printf("%s\n",str[i]); 203 } 204 } 205 206 int main() 207 { 208 freopen("input.txt", "r", stdin); 209 int t;cin>>t; 210 while(t--) 211 { 212 memset(g,0,sizeof(g)); 213 scanf("%d%d", &n,&m); 214 for(int i=0; i<n; i++) 215 { 216 for(int j=0; j<m; j++) 217 { 218 char c=getchar(); 219 if(c=='#') g[i][j]=1; //黑色 220 else if(c=='o') g[i][j]=0; 221 else if(c=='.') g[i][j]=2; 222 else j--; 223 } 224 } 225 cal();print();printf("\n"); 226 } 227 return 0; 228 }