题目大意:给你一个01矩阵,可以进行翻转操作(0变1,1变0,而且一定要成十字,边界不管),最后你要将这个矩阵变成全0矩阵,给出操作矩阵
而且在所有可以的操作矩阵中,找出总操作数最少的,并且字典序最小的。
(我靠,这题意写得,我自己都未必明白)
解题思路:题目看起来不好做,但是注意以下几点就明白了:
1、若第一行的翻转位置确认,则后面所有位置就可以确认;
2、任何位置最多翻转一次,若同一位置翻转2次,就相当于不翻转。
本题所给的矩阵较小,可以枚举第一行的所有翻转情况,逐次验证,并判断翻转次数,存储翻转次数最小的那种情况。
因为可能有次数相同的情况,那么我从右往左枚举,那么次数相同就取先找到的符合要求的情况,即次数相同不予覆盖。
代码:
#include <set> #include <map> #include <list> #include <queue> #include <stack> #include <cmath> #include <string> #include <cstdio> #include <vector> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> //#pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; #define PB push_back #define FOR(i,n,m) for(int i=n;i<=m;i++) #define ROF(i,n,m) for(int i=n;i>=m;i--) #define clr(i,j) memset(i,j,sizeof(i)) #define maxn 17 typedef long long ll; int chess[maxn][maxn]; int temp[maxn][maxn]; int p[maxn][maxn]; int res[maxn][maxn]; int dir[4][2]= {0,1,0,-1,1,0,-1,0}; int m,n; void flip(int x,int y) { temp[x][y]^=1; for(int i=0; i<4; i++) { int t1=x+dir[i][0],t2=y+dir[i][1]; if(t1>0&&t1<m+1&&t2>0&&t2<n+1)temp[t1][t2]^=1; } } int main() { while(~scanf("%d %d",&m,&n)) { for(int i=1; i<=m; i++) for(int j=1; j<=n; j++) scanf("%d",&chess[i][j]); int upper=1<<n; int ans=0,minn=0x7fffffff; for(int k=0; k<upper; k++) { int anw=0; memcpy(temp,chess,sizeof(chess)); for(int i=n-1; i>=0; i--) { p[1][n-i]=(k>>i)&1; if(p[1][n-i]) { flip(1,n-i); anw++; } } for(int i=2; i<=m; i++) { for(int j=1; j<=n; j++) { p[i][j]=temp[i-1][j]; if(p[i][j]) { flip(i,j); anw++; } } } int flag=1; for(int i=1; i<=n; i++) if(temp[m][i]) { flag=0; break; } if(flag) { ans=1; if(anw<minn) { memcpy(res,p,sizeof(p)); minn=anw; } } } if(ans) { for(int i=1; i<=m; i++) { printf("%d",res[i][1]); for(int j=2; j<=n; j++) printf(" %d",res[i][j]); printf("\n"); } } else printf("IMPOSSIBLE\n"); } return 0; }