poj3279 Fliptile

来源:互联网 发布:ubuntu win7 系统时间 编辑:程序博客网 时间:2024/06/05 00:45

题意:给你一个n*m的黑白棋盘,问能否通过操作使得棋盘全变为白色。如果有求一种操作次数最少的方法(相同时要求字典序最小),没有输出"IMPOSSIBLE"。操作为:将某一块和它上下左右全部翻转。

思路:由于我们每次反转都会影响到上面的棋子,我们想全变为白色,我们需要从第二行到第n行每一行把它的上一行的黑色块翻转为白色,如果最后一行把上一行的翻转为白色之后最后一行还有黑色块,证明这种方法不能将棋盘全变白。我们可以发现只要第一行的状态一定,下面的翻法也就一定了。所以我们只需要枚举第一行的所有翻法就可以,我们可以通过用枚举二进制数字的方法来表示翻法。例如:n=5,m=5。00101表示第三块和最后一块进行翻转,此时二进制数最大为11111即(1<<5)-1。

//#include<bits/stdc++.h>#include<iostream>#include<cstdio>#include<cstring>using namespace std;const int INF = 1e9 + 5;const int MAXN = 20;int n, m;bool MAP[MAXN][MAXN], ans[MAXN][MAXN];//地图和答案bool t[MAXN][MAXN], vis[MAXN][MAXN];//临时图和临时答案void FLIPS(int x, int y)//翻{    t[x][y] = !t[x][y];//自己    t[x - 1][y] = !t[x - 1][y];//上    t[x + 1][y] = !t[x + 1][y];//下    t[x][y - 1] = !t[x][y - 1];//左    t[x][y + 1] = !t[x][y + 1];//右}bool judge()//检查最后一行{    for(int i = 1; i <= m; i++)    {        if(t[n][i] == 1) return false;//有黑色的    }    return true;}int work(int cnt)//2-n行翻转{    for(int i = 2; i <= n; i++)    {        for(int j = 1; j <= m; j++)        {            if(t[i - 1][j] == 1)//上一行的j位置为黑,那么就要翻转这一块            {                vis[i][j] = true;                FLIPS(i, j);                cnt++;            }        }    }    if(judge()) return cnt;//最后一行全为白色    return INF;}int main(){    while(cin >> n >> m)    {        //输入        for(int i = 1; i <= n; i++)        {            for(int j = 1; j <= m; j++)            {                cin >> MAP[i][j];            }        }        //求解        int MIN = INF;//存储步数最小值        for(int i = 0; i < (1 << m); i++)//枚举第一行踩的方法        {            memcpy(t, MAP, sizeof(t));//MAP赋给一个临时图t            memset(vis, 0, sizeof(vis));//vis表示踩哪            int cnt = 0;//统计这种方案下需要操作翻转的次数            for(int j = 0; j < m; j++)//对第一行进行操作            {                if(i&(1 << j))                {                    vis[1][j + 1] = true;                    FLIPS(1, j + 1);                    cnt++;                }            }            //work推出下面2-n行的翻转情况            if(work(cnt) < MIN)//更新步数和答案            {                MIN = cnt;                memcpy(ans, vis, sizeof(vis));            }        }        //cout << endl;        if(MIN == INF)//没答案        {            printf("IMPOSSIBLE\n");continue;        }        //输出答案        for(int i = 1; i <= n; i++)        {            for(int j = 1; j <= m; j++)            {                cout << ans[i][j] << " ";            }            cout << endl;        }    }return 0;}/*4 41 0 0 10 1 1 00 1 1 01 0 0 1*/