题目大意:
一个n*m的01矩阵,每次翻转都会连同上下左右的一起翻转(0翻转成1,1变成0),问最少的翻动次数使得矩阵里的1全部变成0
分析:
(1) 对于同一个格子,翻转两次是没有意义的,所以每个格子最多只翻动一次。
(2) 对于第二行,只要第一行的状态确定,第二行的状态也随之确定下来,后面的都能确定,换而言之,只要枚举出第一行的状态,剩下的就都能推出来
(3) 对于第一行,因为只有01两种状态,所以有2^m - 1种方案,可以用二进制下的一个数字表示第一行的状态
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<vector>
using namespace std;
int dist[5][2] = {{0,0},{-1,0},{0,-1},{1,0},{0,1}};
int a[20][20],cal[20][20],opt[20][20];
int n,m,ans;
bool f;
bool find(int x,int y)
{
int res = a[x][y];
for(int i = 0;i < 5; ++i)
{
int xx = x + dist[i][0];
int yy = y + dist[i][1];
if(xx > 0 && xx <= n && yy > 0 && yy <= m)
res += cal[xx][yy];
}
return res % 2;
}
int dfs()
{
for(int i = 2;i <= n; ++i)
for(int j = 1;j <= m; ++j)
if(find(i - 1 , j))
cal[i][j] = 1;
for(int i = 1;i <= m; ++i)
if(find(n,i)) return -1;
int res = 0;
for(int i = 1;i <= n; ++i)
for(int j = 1;j <= m; ++j)
res += cal[i][j];
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n; ++i)
for(int j = 1;j <= m; ++j)
scanf("%d",&a[i][j]);
ans = 0x3f3f3f3f;
for(int i = 0;i < 1 << m; ++i)
{
memset(cal,0,sizeof(cal));
for(int j = 1;j <= m; ++j)
cal[1][j] = i >> (m - j) & 1;
int s = dfs();
if(s >= 0 && s < ans)
{
ans = s;
memcpy(opt,cal,sizeof(opt));
f = 1;
}
}
if(f)
{
for(int i = 1;i <= n; ++i)
{
for(int j = 1;j <= m; ++j)
{
if(j > 1) printf(" ");
printf("%d",opt[i][j]);
}
printf("\n");
}
}
else printf("IMPOSSIBLE\n");
return 0;
}