题目的意思大概是这样的:
给你一个n*m的图,把图中所有的1变成0,每次你可以选择一个“十”字型范围进行反转(0变1 ,1变0),
如
1 1 1
1 1 1
1 1 1
在中间位置翻转一次会变成
1 0 1
0 0 0
1 0 1
问你最少需要多少次才能完成,并输出最少翻转的位置,(多组输出字典序)
思路:
如果直接尝试枚举每个位置的翻转,那么需要复杂度为O2^(m*n),过不去的,但是仔细思考后你会发现,如果第一行的翻转是指定的话,那么这个图能不能被全部反转为0就已经确定了。
因此,思路为:枚举第0行的所有翻转方式,然后从第1行开始,若某个点上方那个点需要被反转则反转这个点,同时记录反转过程中反转最少的一次即可
代码如下:
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
int n,m,Map[20][20],temp[20][20];//Map用来保存初始状态,temp用来保存枚举第一行反转形式后的状态
int current[20][20],ans[20][20];//current用来枚举第一行反转形式后反转的位置,ans用来储存答案(最少反转策略)
void change(int x,int y){//反转这个点和它挨着的四个点
temp[x][y]=!temp[x][y];
if(x-1>=0)temp[x-1][y]=!temp[x-1][y];
if(x+1<n)temp[x+1][y]=!temp[x+1][y];
if(y-1>=0)temp[x][y-1]=!temp[x][y-1];
if(y+1<m)temp[x][y+1]=!temp[x][y+1];
}
int solve(){
int ok=1000;
for(int k=0;k<(1<<m);k++){
//printf("* * * %d\n",k);
for(int i=0;i<n;i++)for(int j=0;j<m;j++)temp[i][j]=Map[i][j];
memset(current,0,sizeof(current));
int Max=0;
for(int j=0;j<m;j++){
current[0][(m-1)-j]=(k>>j)&1;
if(current[0][(m-1)-j]){
Max++;
change(0,(m-1)-j);
}
}
for(int i=1;i<n;i++){
for(int j=0;j<m;j++){
if(temp[i-1][j]){
Max++;
change(i,j);
current[i][j]=1;
}
}
}
int t;
/*for(int i=0;i<n;i++){
for(int j=0;j<m;j++)printf("%d ",current[i][j]);
printf("\n");
}
printf("\n");*/
for(t=0;t<m;t++)if(temp[n-1][t])break;
if(t==m && ok>Max){
ok=Max;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++)ans[i][j]=current[i][j];
}
}
}
if(ok==1000)return false;
return true;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
for(int j=0;j<m;j++)scanf("%d",&Map[i][j]);
}
if(solve()){
for(int i=0;i<n;i++){
for(int j=0;j<m;j++)printf("%d ",ans[i][j]);
printf("\n");
}
}
else printf("IMPOSSIBLE\n");
}