链接: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?' ':' '); } 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; }