题目描述
PIPI有一个n*m的01矩阵。每次操作PIPI都可以任选一个格子使其翻转(即0->1或1->0),同时其上下左右的格子(如果存在)也会跟着翻转。
PIPI想知道最少操作多少次才能使得矩阵中的值全部为0?
请你输出操作次数最少的方案,如果答案不唯一,输出字典序最小的一组。如果没有合法的方案,输出“IMPOSSIBLE”。
PS:这里的字典序指的是将你输出的01矩阵逐行拼接在一起形成的01串的字典序。
输入
第一行两个正整数n,m.(n<=15,m<=15)
接下里n行给出一个n*m的01矩阵。
输出
如果有合法方案,输出n行,每行m个数,每个数代表该位置的翻转次数。
若无答案,输出“IMPOSSIBLE”。
样例输入
4 4
1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1
样例输出
0 0 0 0
1 0 0 1
1 0 0 1
0 0 0 0
代码引用自hx
思路:每个要求的点的值与他的上下左右与原数组的该点的值有关,如果和为奇数则该店的下面要求的那个点的值为1,否则为0
#include<bits/stdc++.h>
using namespace std;
//t表示最小操作次数,初值置为无限大
int t=1e9,n,m,dx[]={0,0,0,-1},dy[]={0,1,-1,0};
//R为初始矩阵,T为操作矩阵,ans为答案矩阵
bool R[20][20],T[20][20]={0},ans[20][20];
//得到(x,y)的状态
bool get(int x,int y)
{
int s=R[x][y],i,X,Y;
for(i=0;i<4;i++)
{
X=x+dx[i],Y=y+dy[i];
if(X>=0&&X<n&&Y>=0&&Y<m)s+=T[X][Y];
}
return s%2;
}
//DFS枚举操作数组第一行状态
void DFS(int L)
{
//flag表示操作数组是否合法,k统计合法T的操作次数
int i,j,k,flag;
//枚举完毕
if(L==m-1)
{
//递推剩下n-1行状态
for(i=1;i<n;i++)
for(j=0;j<m;j++)T[i][j]=get(i-1,j);
//检查最后一行状态是否合法,即要求全为0
for(flag=1,i=0;flag&&i<m;i++)if(get(n-1,i))flag=0;
//若非法直接return
if(!flag)return;
//统计操作次数,也就是T矩阵中1的个数
for(k=i=0;i<n;i++)
for(j=0;j<m;j++)k+=T[i][j];
//若操作次数比最小值更小,更新t与ans矩阵
if(k>=t)return;
t=k;
for(i=0;i<n;i++)
for(j=0;j<m;j++)ans[i][j]=T[i][j];
return;
}
for(i=0;i<2;i++)T[0][L+1]=i,DFS(L+1);
}
int main()
{
int i,j;
scanf("%d%d",&n,&m);
for(i=0;i<n;i++)
for(j=0;j<m;j++)scanf("%d",&R[i][j]);
//按字典序开始枚举第一行状态
for(i=0;i<2;i++)T[0][0]=i,DFS(0);
//若t为无限大,表示无解
if(t==1e9)printf("IMPOSSIBLE\n");
else
{
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
if(j)printf(" ");
printf("%d",ans[i][j]);
}
printf("\n");
}
}
return 0;
}