给你一个01矩阵,矩阵大小为M x N。(1 <= M , N <= 15)
每次操作选择一个格子,使得该格子与上下左右四个格子的值翻转。
至少多少次操作可以使得矩阵中所有的值变为0?
请输出翻转方案,若没有方案,输出"IMPOSSIBLE” 。
Input
第一行输入两个数:M和N。(1 <= M , N <= 15)
接下来M行,每行N个数,其值只为0或1。
Output
输出M行,每行N个数。
每个数代表该位置翻转次数
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
刚看题毫无头绪,既要反转次数最少又要字典序最小,暴力枚举复杂度得2^(m*n),太大了,状压也不知道该怎么做。
仔细观察可以发现,要想使矩阵全部为0,那么每个1都要反转,而且每个位置最多反转一次,如果反转两次就又回去了做的是无用功。既然每个1都要反转,那么对于[i,j]这个位置,如果[i-1,j]为1的话,[i,j]就必须要反转,所以上一行的状态确定了,这一行必须反转的位置就也确定了,所以第1行的状态确定了,第2行就确定了,那第3行业确定了…,那么总的反转次数就确定了。
所以首先要确定第一行的状态(第一行不一定全为0,因为第二行的时候会把第一行全变为0),这样到第最后一行的时候前面全部变为0了,但最后一行不一定,判断一下即可。
还有一个条件是反转的字典序最小,因为第一行状态确定,后面所有行的饭庄位置就确定了,所以第一行的反转状态字典序最小整个的字典序就最小了,所以从小到大枚举第一行的反转状态,取反转次数最小的那个。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=100;
int n,m,a[N][N],cnt[N][N],num[N][N]; //cnt保存当前状态的反转矩阵,num保存最终的答案矩阵
void reverse(int x,int y)//以x,y为中心反转
{
a[x][y]=1-a[x][y];
if(x>1) a[x-1][y]=1-a[x-1][y];
if(y>1) a[x][y-1]=1-a[x][y-1];
if(y<m) a[x][y+1]=1-a[x][y+1];
if(x<n) a[x+1][y]=1-a[x+1][y];
}
int dfs(int x,int y)
{
int t=0;//统计反转次数
if((x==n+1))
{
bool flag=1;
for(int j=1;j<=m;j++) if(a[n][j]) flag=0;
if(cnt[x][y]) reverse(x,y);//将矩阵恢复原样
if(flag) return t;
else return 0x3f3f3f3f;//如果不可行返回0x3f3f3f3f这个状态的次数就一定不会比答案tmp小,就不会出错了
}
if(a[x-1][y])
{
reverse(x,y);
t++;
cnt[x][y]=1;
}
if(y<m) t+=dfs(x,y+1);
else t+=dfs(x+1,1);
if(cnt[x][y]) reverse(x,y);//恢复矩阵
return t;
}
int tmp=0x3f3f3f3f;
void solve(int i,int t)//正向遍历第一行,第一次判断的反转状态是0000,然后递归回m,这是的状态是0001,再递归回去就是0010,然后0011...就实现了从小到大枚举第一行的状态
{
if(i==m+1)
{
for(int j=2;j<=n;j++)//将第二行一下的cnt清空
for(int k=1;k<=m;k++) cnt[j][k]=0;
t+=dfs(2,1);//计算第2行以后的反转次数
if(t<tmp)
{
for(int j=1;j<=n;j++)//保存反转矩阵
for(int k=1;k<=m;k++)
num[j][k]=cnt[j][k];
tmp=t;
}
return ;
}
solve(i+1,t);
reverse(1,i);
cnt[1][i]=1;
solve(i+1,t+1);
reverse(1,i);//记得恢复现场
cnt[1][i]=0;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j];
solve(1,0);
if(tmp<0x3f3f3f3f)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
cout<<num[i][j]<<' ';
puts("");
}
}
else puts("IMPOSSIBLE");
}