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 }

 

posted @ 2021-01-08 22:04  acmloser  阅读(73)  评论(0编辑  收藏  举报