poj 3279 Fliptile(搜索)

题目大意:

        有一个m*n的棋盘,每个格子上是0或1,每次可以对一个格子做一次翻转操作,将被操作的格子和上下左右4个格子的0/1翻转。问做少做多少次翻转可以将所有格子翻转成0,输出翻转方案。没有方案时输出“IMPOSSIBLE”。

解题思路:

        枚举第一行的共2^n种翻转方式。
        枚举第一行的情况相当于确定了整个棋盘的翻转方式,因为在第i行翻转方式确定之后那么在翻转第i+1行时就一定要使第i行全为0。在全部翻转结束后根据判断最后一行是否全为0就可以确定这是不是一个有效解。

代码:

#include 
#include 

using namespace std;

int a[20][20],b[20][20];
int f[20][20],ans[20][20],fNum,aNum,n,m;

//翻转
void tranc(int x,int y){
    fNum ++;
    f[x][y] = 1;
    b[x][y] = 1-b[x][y];
    b[x-1][y] = 1-b[x-1][y];
    b[x+1][y] = 1-b[x+1][y];
    b[x][y-1] = 1-b[x][y-1];
    b[x][y+1] = 1-b[x][y+1];
}

//记录答案
void record(){
    aNum = fNum;
    for(int i = 1; i <= m; i ++){
        for(int j = 1; j <= n; j ++){
            ans[i][j] = f[i][j];
        }
    }
}

int main()
{
    //输入数据
    cin >> m >> n;
    for(int i = 1; i <= m; i ++){
        for(int j = 1; j <= n; j ++){
            cin >> a[i][j];
        }
    }
    int t = 1 << n;
    aNum = 10000;
    //尝试第一行的所有可能
    for(int loop = 0; loop < t;  loop ++){
        for(int i = 1; i <= m; i ++){
            for(int j = 1; j <= n; j ++){
                b[i][j] = a[i][j];
            }
        }
        fNum = 0;
        int moveN = loop;
        memset(f,0,sizeof(f));
        //翻转第一行
        for(int j = 1; j <= n; j ++){
            if(moveN & 1) tranc(1,j);
            moveN = moveN >> 1;
        }
        //翻转2-m行
        for(int i = 2; i <= m; i ++){
            for(int j = 1; j <= n; j ++){
                if(b[i-1][j]) tranc(i,j);
            }
        }
        //判断这种翻转方式是否合法
        int j;
        for(j = 1; j <= n; j ++){
            if(b[m][j]) break;
        }
        if(j == n + 1 && fNum < aNum) record();
    }
    //不合法
    if(aNum == 10000){
        cout << "IMPOSSIBLE" << endl;
        return 0;
    }
    //合法
    for(int i = 1; i <= m; i ++){
        cout << ans[i][1];
        for(int j = 2; j <= n; j ++){
            cout << " " << ans[i][j];
        }
        cout << endl;
    }
    return 0;
}


你可能感兴趣的:(poj,搜索)