题目链接:点击打开链接
题意描述:给定一个m*n的格子,每个格子要么为黑,要么为白,我们可以通过反转格子使颜色变为相反的颜色,反转x,y时,顺便会把相邻的四个格子的颜色改变,问最少需要反转多少次能使所有格子变为白色,并输出反转情况?
解题思路:典型的反转(开关问题)
分析:枚举第一行的所有情况,对于第一行已经确定后,第二行···也就随之确定了
代码:
#include <cstdio>
#include <cstring>
#define INF 0x7fffffff
#define MAXN 17
using namespace std;
const int dx[5]={-1,0,0,0,1};
const int dy[5]={0,-1,0,1,0};
int m,n,res,num;
int g[MAXN][MAXN];
int opt[MAXN][MAXN];///保存最优解
int flip[MAXN][MAXN];///保存中间结果
int get(int x,int y){
int c=g[x][y];
for(int d=0;d<5;++d) c+=flip[(x+dx[d])][(y+dy[d])];
return c&1;
}
void calc(){
for(int i=2;i<=m;++i) for(int j=1;j<=n;++j)
if(get(i-1,j)){
flip[i][j]=1;num++;
if(num>=res) {num=INF;return;}
}
for(int j=1;j<=m;++j)if(get(m,j)){
num=INF;return;
}
}
bool solve(){
res=INF;
int sizet=1<<n;
for(int i=0;i<sizet;++i){
if(res<=1) return true;
memset(flip,0,sizeof(flip));
num=0;
for(int j=1;j<=n;++j)
if(i>>(n-j)&1){
flip[1][j]=1;
num++;
if(num>=res) break;
}
if(num>=res) continue;
calc();
if(num<res){
res=num;
memcpy(opt,flip,sizeof(flip));
}
}
if(res==INF) return false;
else return true;
}
int main(){
while(scanf("%d%d",&m,&n)!=EOF){
for(int i=1;i<=m;++i) for(int j=1;j<=n;++j)
scanf("%d",&g[i][j]);
if(!solve()) {printf("IMPOSSIBLE\n");continue;}
for(int i=1;i<=m;++i){
for(int j=1;j<n;++j)
printf("%d ",opt[i][j]);
printf("%d\n",opt[i][n]);
}
}
return 0;
}