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