链接:http://poj.org/problem?id=3279
上一行的的方块只能通过点击下一行改变
枚举第一行的所有反转方式。 开关问题
#include <iostream>
#include<cstring>
#include<cstdio>
#define MAX_N 20
#define MAX_M 20
using namespace std;
const int dx[5]={-1,0,0,0,1};
const int dy[5]={0,-1,0,1,0};
int M,N;
int tile[MAX_M][MAX_N];
int opt[MAX_M][MAX_N]; //保存最优解
int flip[MAX_M][MAX_N]; //保存中间结果
int get(int x,int y) //查询 x,y的颜色
{
int c=tile[x][y];
for(int d=0;d<5;d++)
{
int nx=x+dx[d];
int ny=y+dy[d];
if(nx>=0&&ny>=0&&nx<M&&ny<N)
c+=flip[nx][ny];
}
return c%2;
}
int calc() //求出第一行确定情况下的最小操作次数
{
//求出从第二行开始的反转方法
for(int i=1;i<M;i++)
for(int j=0;j<N;j++)
if(get(i-1,j)!=0) //上一行只能通过下一行改变
flip[i][j]=1;
for(int j=0;j<N;j++) //判断最后一行是否全白
if(get(M-1,j)!=0)
return -1;
int res=0;
for(int i=0;i<M;i++)
for(int j=0;j<N;j++)
res+=flip[i][j];
return res;
}
void solve()
{
int res=-1;
//字典序尝试第一行所有可能性
for(int i=0;i<1<<N;i++)
{
memset(flip,0,sizeof(flip));
for(int j=0;j<N;j++)
flip[0][N-j-1]=i>>j&1; //将i的二进制写入flip[0]
int num=calc();
if(num>=0&&(res<0||res>num))
{
res=num;
memcpy(opt,flip,sizeof(flip));
}
}
if(res<0)
cout<<"IMPOSSIBLE"<<endl;
else
for(int i=0; i<M; i++)
for(int j=0; j<N; j++)
printf("%d%c",opt[i][j],j+1==N?'\n':' ');
}
int main()
{
while(cin>>M>>N)
{
for(int i=0;i<M;i++)
for(int j=0;j<N;j++)
cin>>tile[i][j];
solve();
}
return 0;
}