题意:
给一个01矩阵,求它经过最少多少次翻转能得到全0矩阵,翻转(i,j)后(i,j)四周的点也会被翻转。
分析:
因为第一行的翻转情况确定后为达到目标矩阵之后各行的翻转情况就确定了,所以枚举第一行的m个点的所有子集进行翻转,求可以达到目标矩阵的最小翻转数。
代码:
//poj 3279
//sepNINE
#include <iostream>
using namespace std;
int mat[32][32];
int c[32][32],record[32][32],ansMat[32][32];
int m,n,ans;
void change(int i,int j){
c[i][j]=1-c[i][j];
if(i-1>=0)
c[i-1][j]=1-c[i-1][j];
if(i+1<m)
c[i+1][j]=1-c[i+1][j];
if(j-1>=0)
c[i][j-1]=1-c[i][j-1];
if(j+1<n)
c[i][j+1]=1-c[i][j+1];
}
int flip(int s)
{
int i,j,tot=0;
memset(c,0,sizeof(c));
memset(record,0,sizeof(record));
for(i=n-1;i>=0;--i){
int x=s&1;
s=s/2;
if(x==1){
change(0,i);
record[0][i]=1;
++tot;
}
}
for(i=1;i<m;++i)
for(j=0;j<n;++j)
if(c[i-1][j]!=mat[i-1][j]){
change(i,j);
record[i][j]=1;
++tot;
}
for(j=0;j<n;++j)
if(c[m-1][j]!=mat[m-1][j])
return -1;
if(tot<ans){
ans=tot;
for(i=0;i<m;++i)
for(j=0;j<n;++j)
ansMat[i][j]=record[i][j];
}
return 1;
}
int main()
{
scanf("%d%d",&m,&n);
int i,j;
for(i=0;i<m;++i)
for(j=0;j<n;++j)
scanf("%d",&mat[i][j]);
ans=INT_MAX;
int flag=0;
for(i=0;i<(1<<n);++i)
if(flip(i)==1)
flag=1;
if(flag==0)
printf("IMPOSSIBLE\n");
else
for(i=0;i<m;++i){
for(j=0;j<n;++j)
printf("%d ",ansMat[i][j]);
printf("\n");
}
return 0;
}