题意

给定一张n*m的方格图,有1,0两种数字,每次可以选取一个十字进行翻转,1变成0,0变成1,问最少需要翻转几次,使它全部变成0,全部如果有重复的,按字典序最小的进行输出;

输入

第一行n,m

下面一个矩阵,代表方格图

输出

按矩阵原格式输出是否需要翻转

Sample Input

4 4
1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1

Sample Output

0 0 0 0
1 0 0 1
1 0 0 1
0 0 0 0

分析

这道题也是一个开关问题,但是相对于上一题(我博客的上一题[POJ3276]),显然这是个二维的矩阵了,调整一个会对四面八方有影响,这样就不能确定地做一件操作的。我们要想着如何变成一个有固定操作规律的方法。看数据也许能启发我们,因为N只有15,可能跟搜索有关。我们有以往的例子,在列用dfs枚举,在行就可以直接操作/DP。针对这个题,发现如果第一行确定了,第一行的某个值确定了,就只有它正下方的数字影响它,这样就可以确定下一行的翻转情况。以此类推,检验最后一行即可。

代码

 #include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define RG register int
#define rep(i,a,b) for(RG i=a;i<=b;++i)
#define per(i,a,b) for(RG i=a;i>=b;--i)
#define ll long long
#define inf (1<<29)
#define maxn 20
using namespace std;
int n,m,ans=inf;
int num[maxn][maxn],gra[maxn][maxn],tag[maxn][maxn],rec[maxn],an[maxn][maxn];
inline int read()
{
int x=,f=;char c=getchar();
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
} inline void modify(int i,int j){gra[i][j]^=,gra[i-][j]^=,gra[i][j-]^=,gra[i+][j]^=,gra[i][j+]^=;}
int p;
void judge()
{
memset(tag,,sizeof(tag));
rep(i,,n)rep(j,,m) gra[i][j]=num[i][j];
rep(i,,m) if(rec[i]) tag[][i]=,modify(,i);
rep(i,,n)
rep(j,,m)
if(gra[i-][j])
modify(i,j),tag[i][j]=;
rep(j,,m) if(gra[n][j]) return;
int sum=;
rep(i,,n)rep(j,,m) sum+=tag[i][j];
if(sum<=ans)
{
ans=sum;
rep(i,,n)rep(j,,m) an[i][j]=tag[i][j];
}
} void dfs(int step)
{
if(step>m){judge();return;}
rec[step]=;dfs(step+);
rec[step]=;dfs(step+);
} int main()
{
n=read(),m=read();
rep(i,,n)rep(j,,m) num[i][j]=read();
dfs();
if(ans==inf)puts("IMPOSSIBLE");
else {rep(i,,n){rep(j,,m) printf("%d ",an[i][j]);puts("");}}
return ;
}
05-08 08:34