转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents by---cxlove
对于2*n的矩形,每次能选择子矩形进行染色,问达到最终要求的最小步数。
每一步选择一个位置,进行扩展,将尽可能大的部分进行标记,如果遇到已经和目标颜色一样的位置,则跳出。最后把每一步的所有子集都加入到队列当中,总共最多16个方格,如果与目标颜色一样用1表示,否则为0,这样使用一个16位的二进制数便可以保存。
对于子集来说,每次减一再与便可以,这样可以每次减少一个1。
扩展包括两种情况,一种是单行的,往两边扩展。还有一种是双行的,只需要考虑上一行为中心即可,因为下一行的情况已经考虑。
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<string> using namespace std; struct Node{ int val,step; //当前情况与步数 }s,e,u,v; char str[20]; bool flag[1<<16]; //16位二进制数表示,是否与目标颜色一致 int n; int bfs(){ queue<Node>que; while(!que.empty()) que.pop(); que.push(s); memset(flag,false,sizeof(flag)); flag[0]=true; while(!que.empty()){ u=que.front(); que.pop(); if(u.val==(1<<(2*n))-1) //所有位置全部和目标颜色一样 return u.step; for(int i=0;i<2*n;i++){ v=u;v.step++; if((1<<i)&u.val) continue; //当前已经为目标色 int tmp=0; for(int j=i;j<(i/n+1)*n;j++){ //单行往右扩展 if((1<<j)&u.val) break; if(str[j]==str[i]) tmp|=1<<j; } for(int j=i-1;j>=(i/n)*n;j--){ //单行往左扩展 if((1<<j)&u.val) break; if(str[j]==str[i]) tmp|=1<<j; } for(int j=tmp;j;j=tmp&(j-1)){ //将所有子集添加 if(flag[u.val|j]) break; flag[u.val|j]=true; v.val=(u.val|j); que.push(v); } if(i>=n) //只需要考虑第一行,避免还要分情况考虑 continue; if(u.val&(1<<(i+n))) continue; tmp=0; for(int j=i;j<n;j++){ if(((1<<j)&u.val)||((1<<(j+n))&u.val)) break; if(str[j]==str[i]) tmp|=(1<<j); if(str[j+n]==str[i]) tmp|=(1<<(j+n)); } for(int j=i-1;j>=0;j--){ if(((1<<j)&u.val)||((1<<(j+n))&u.val)) break; if(str[j]==str[i]) tmp|=(1<<j); if(str[j+n]==str[i]) tmp|=(1<<(j+n)); } for(int j=tmp;j;j=tmp&(j-1)){ if(flag[u.val|j]) continue; flag[u.val|j]=true; v.val=(u.val|j); que.push(v); } } } } int main(){ int t,cas=0; scanf("%d",&t); while(t--){ scanf("%d",&n); scanf("%s%s",str,str+n); s.val=s.step=0; printf("Case #%d: %d\n",++cas,bfs()); } return 0; }