题目大意:给你一个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;
}