题目链接:https://vjudge.net/contest/294982#problem/C
题意:给定由0,1组成的m*n型矩阵,每次翻转一个结点会顺带翻转与其相邻的结点,问将矩阵翻转为全0的最小步骤,若有多解,输出字典序最小的(将矩阵看成字符串)。
思路:刚开始用搜索一个结点一个结点地搜,老段错误。后来在网上查了后发现这题好象不是搜索题,首先要知道每个结点最多翻一次,因为翻2次和0次一样,3次和1次一样。因为第一行总共有1<<n种翻法,而只要第一行的翻法确定了之后,后面的翻法也就确定了,后面的翻法为若上一个结点为1,则翻转,这样可将前m-1行全部翻成0,最后只用判断最后一行是否全为0,如果是则这种翻法可行。如果我们在第一行的翻法从0到1<<n-1依次枚举,即按字典序枚举,然后只需要找翻转次数最少的,即可得到结果。
AC代码:
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 5 const int inf=0x3f3f3f3f; 6 int m,n,Min,cnt,a[20][20],ans[20][20],b[20][20],c[20][20]; 7 int go[5][2]={0,0,-1,0,0,1,1,0,0,-1}; 8 9 void flip(int x,int y){ 10 for(int i=0;i<5;++i){ 11 int xx=x+go[i][0],yy=y+go[i][1]; 12 if(xx>=0&&xx<m&&yy>=0&&yy<n) 13 b[xx][yy]=1-b[xx][yy]; 14 } 15 } 16 17 bool check(){ 18 bool ret=true; 19 for(int i=0;i<n;++i) 20 if(b[m-1][i]){ 21 ret=false; 22 break; 23 } 24 return ret; 25 } 26 27 void dfs(int num){ 28 if(num==m) return; 29 for(int i=0;i<n;++i) 30 if(b[num-1][i]){ 31 ++cnt; 32 flip(num,i); 33 c[num][i]=1; 34 } 35 dfs(num+1); 36 } 37 38 int main(){ 39 scanf("%d%d",&m,&n); 40 for(int i=0;i<m;++i) 41 for(int j=0;j<n;++j) 42 scanf("%d",&a[i][j]); 43 Min=inf; 44 for(int i=0;i<(1<<n);++i){ 45 memcpy(b,a,sizeof(a)); 46 memset(c,0,sizeof(c)); 47 cnt=0; 48 for(int j=0;j<=n-1;++j) 49 if(i&(1<<j)){ 50 ++cnt; 51 flip(0,n-j-1); 52 c[0][n-j-1]=1; 53 } 54 dfs(1); 55 if(check()){ 56 if(Min>cnt){ 57 Min=cnt; 58 memcpy(ans,c,sizeof(c)); 59 } 60 } 61 } 62 if(Min==inf) 63 printf("IMPOSSIBLE "); 64 else 65 for(int i=0;i<m;++i){ 66 for(int j=0;j<n;++j){ 67 printf("%d",ans[i][j]); 68 if(j!=n-1) printf(" "); 69 } 70 printf(" "); 71 } 72 return 0; 73 }