【解题报告】POJ - 3279 Fliptile (状态压缩、枚举)

原题地址

题意

这是一道非常好的题目,可以说是为棋盘问题提供了一个很好的思路,也是状态压缩+枚举的一道入门的好题。
题意大致是说有一张m×n大小的只有0和1的棋盘,每一次可以在棋盘中选一个点,将该点和与该点相邻的点与1异或(如果是0就变成1,如果是1就变成0)问是否有解能时棋盘上的点全部变成0,如果有解,需要输出最少操作的解,如果最少操作的解有多个,那么要输出将棋盘视作字符串之后字典序最新小的解。

思路

一开始我想的是,每一次都只翻转值为1的点,后来觉得不对,又想枚举每一个点,如果翻转该点的贡献为正那么就翻转否则就不翻转,后来发现这些解法都不太对,而且很难实现,所以就去看了一下dalao们的解法,才知道了一种很奇特的方案。我们只用考虑第一排的翻转方式,如果第一排确定了,那么只能通过第二排才能将第一排变成1的点翻转成0,以此类推,以后的每一排都要按照它的上一排的状态进行翻转,直到最后一排,如果最后一盘根据倒数第二排的状态翻转自己后不是全为0,那么证明无解,否则就是一个解。(这和那个停车的题很类似,都是枚举第一排的状态这样就可以依次确定下面若干排的状态)这个时候就可以用到状态压缩,我之前看到这个算法的时候还并不是很理解,做完这道题之后差不多懂了一些。从0到2^m-1枚举第一排的翻转情况,这样就可以保证考虑到了每一次可能的操作,i的二进制的每一位代表对第一排每一列的操作,1表示翻转该列的第一个点,0代表不翻转,然后再往下递推,确定所有点的翻转情况,最后判断是否构成一个可能的解。

#include <iostream>
#include <cstring>
#define maxn 20
using namespace std;
int blank[maxn][maxn], opt[maxn][maxn], ans[maxn][maxn], cur[maxn][maxn];
int minstep = 1<<30, step = 0;
int n, m;
void press(int i, int j)
{
    cur[i][j] ^= 1;
    cur[i+1][j] ^= 1;
    cur[i-1][j] ^= 1;
    cur[i][j+1] ^= 1;
    cur[i][j-1] ^= 1;
}
bool solve()
{
    memcpy(cur, blank, sizeof(cur));
    for(int i=1; i<=m; i++)
    {
        if(opt[1][i])
        {
            press(1, i);
            step++;
        }
    }
    for(int i=2; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            if(cur[i-1][j])
            {
                press(i, j);
                step++;
                opt[i][j]++;
            }
        }
    }
    for(int i=1; i<=m; i++)
    {
        if(cur[n][i])
        {
            return false;
        }
    }
    return true;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=m; j++)
        {
            cin>>blank[i][j];
        }
    }
    for(int i=0; i < (1<<m); i++)
    {
        memset(opt, 0, sizeof(opt));
        step = 0;
        for(int j=0; j<m; j++)
        {
            if(i>>j & 1)
            {
                opt[1][m-j] = 1;
            }
            else
            {
                opt[1][m-j] = 0;
            }
        }
        if(solve() && step >= 0 &&  step < minstep)
        {
            minstep = step;
            memcpy(ans, opt, sizeof(ans));
        }
    }
    if(minstep < (1<<30))
    {
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=m; j++)
            {
                cout<<ans[i][j]<<" ";
            }
            cout<<endl;
        }
    }
    else
        cout<<"IMPOSSIBLE"<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42772300/article/details/88600908