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