Fliptile POJ - 3279
考察:一点点位运算+枚举+递推
好像也可以用广搜写,但这里有个技巧真心妙
易错:
矩阵要求的字典序是从右往左最小
从Y总来的思路:
当我们确定第一行的状态时,第二行要必须灭掉第一行的1,再因为我们需要最短次数.根据第二行,我们就确定了第三行的状态,以此类推.所以我们确定了第一行的状态就确定了所有行的状态.枚举第一行的状态,每个0和1都可以选择按或者不按.共2^n次枚举
注意:
最后一行也要按,否则WA
算法竞赛进阶指南上有递归实现指数型枚举,但是这样我们可以利用位运算
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 const int N = 18; 5 int mp[N][N],m,n,xx[5]={-1,1,0,0,0},yy[5]={0,0,-1,1,0}; 6 int ans[N][N],cnt[N][N],res,minv = 0x3f3f3f; 7 bool flag = true; 8 void turnup(int x,int y) 9 { 10 cnt[x][y]=1; 11 res++; 12 for(int i=0;i<5;i++) 13 { 14 int dx = x+xx[i],dy = y+yy[i];//超界问题没有考虑 15 if(dx>=0&&dx<m&&dy>=0&&dy<n) { mp[dx][dy]^=1;} 16 } 17 } 18 bool check() 19 { 20 for(int i=0;i<n;i++) if(mp[m-1][i]) return false; 21 return true; 22 } 23 void work() 24 {//枚举第一行按的情况,不管是0还是1都按 25 int backup[N][N]; 26 memcpy(backup,mp,sizeof(mp)); 27 for(int i=0;i<(1<<n);i++) 28 { 29 res = 0; 30 memset(cnt,0,sizeof(cnt)); 31 for(int j=0;j<n;j++)//字典序最小,即找到了当即跳出 32 if(i>>j&1) turnup(0,j); 33 for(int j=1;j<m;j++)//1~m-2行 34 for(int k=0;k<n;k++) 35 if(mp[j-1][k]) turnup(j,k); 36 if(check()) 37 { 38 if(res<minv){ 39 minv = res; 40 memcpy(ans,cnt,sizeof(cnt)); 41 } 42 } 43 memcpy(mp,backup,sizeof(backup)); 44 } 45 } 46 int main() 47 { 48 // freopen("in.txt","r",stdin); 49 scanf("%d%d",&m,&n); 50 for(int i=0;i<m;i++) 51 for(int j=0;j<n;j++) 52 scanf("%d",&mp[i][j]); 53 work(); 54 if(minv==0x3f3f3f) { printf("IMPOSSIBLE\n"); return 0; } 55 for(int i=0;i<m;i++){ 56 for(int j=0;j<n;j++){ 57 printf("%d ",ans[i][j]); 58 } 59 printf("\n"); 60 } 61 return 0; 62 }