POJ-3279 Fliptile 【状态压缩+DFS+枚举】

版权声明:本文为博主原创文章,转载请注明出处( • ̀ω•́ )✧ https://blog.csdn.net/wangws_sb/article/details/83097695

题目传送门

题目大意:有一个 M * N 的格子,每个格子可以翻转正反面,它们有一面是黑色,另一面是白色。黑色翻转之后变成白色,白色翻转之后则变成黑色。游戏要做的是把所有的格子翻转为白色。不过每次翻转一个格子,与它上下左右相邻接的格子也会被翻转。求总翻转次数最少时,每个格子的翻转次数。最少翻转次数有多个时,输出字典序最小的一组;解不存在的话,则输出IMPOSSIBLE

题目样例:0表示白色,1表示黑色

            1 0 0 1
            0 1 1 0
            0 1 1 0
            1 0 0 1

题目思路:
首先,枚举第一行的翻转方法。此时,能翻转(0,0)的只剩下了(1,0),所以可以直接判断(1,0)是否需要翻转。类似的(1,0)~(M-1,N-1)都能这样判断,如此反复下去就能确定所有格子的翻转方法,最后判断(M-1,0)~(M-1,N-1)是否全为白色,若不是则意味着不存在可行的操作方法。像这样,先确定第一行的翻转方式,然后可以很容易判断这样是否存在解以及解的最步数是多少,这样将第一行的所有翻转方式都尝试一次就能求出整个问题的最小步数。这个算法中最上面 一行的翻转方式共有2^N种
复杂度为O(M * N * 2^N)

AC代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
using namespace std;
int M,N;
const int MAX=16;
int dx[5]={-1,0,0,0,1};
int dy[5]={0,-1,0,1,0};
int tile[MAX][MAX];//存放原始数组
int opt[MAX][MAX];//保存最优解
int file[MAX][MAX];//保存当前解法
int get(int x,int y)//查询当前第(x,y)个格子的颜色
{
    int c=tile[x][y];//自己本身的颜色
    for(int i=0;i<5;i++)//查询周围以及自己的翻转次数,一个格子的翻转次数等于它本身的翻转次数加上它周围格子的翻转次数,因为它周围的格子翻转时它也会翻转
    {
        int tx=x+dx[i];
        int ty=y+dy[i];
        if(tx>=0&&tx<M&&ty>=0&&ty<N)
            c+=file[tx][ty];
    }
    return c%2;//奇数为1(黑色),偶数为0(白色)
}
int cale()
{
    int res=0;
    for(int i=1;i<M;i++)
    {
        for(int j=0;j<N;j++)
        {
            if(get(i-1,j)==1)//如果当前第(i-1,j)个格子是黑色的话,必须翻转这个格子
            {
                file[i][j]=1;
            }
        }
    }
    for(int i=0;i<N;i++)//判断最后一行格子是否全为白色
    {
        if(get(M-1,i)==1)//若不是,则无解
            return -1;
    }
    for(int i=0;i<M;i++)//统计翻转次数
    {
        for(int j=0;j<N;j++)
        {
            res+=file[i][j];
        }
    }
    return res;
}
void solve()
{
    int res=-1;
    //按字典序尝试第一行的所有可能性
    for(int i=0;i<1<<N;i++)//i表示一个二进制数,用来枚举第一行的各种不同翻法
    {
        memset(file,0,sizeof(file));
        for(int j=0;j<N;j++)
        {
            file[0][N-1-j]=(i>>j)&1;//每次取出最后一位存入file中,表示第一行的翻转情况
        }
        int num=cale();//记录每种翻法的翻转次数
        if(num>=0&&(res==-1||res>num))//记下翻转次数最少的翻法
        {
            res=num;
            memcpy(opt,file,sizeof(file));//将翻转次数最少的翻法保存在opt中
        }
    }
    if(res==-1)//无解
    {
        cout<<"IMPOSSIBLE"<<endl;
    }
    else
    {
        for(int i=0;i<M;i++)//输出翻转次数最少的翻法
        {
            for(int j=0;j<N;j++)
            {
                cout<<opt[i][j]<<" ";
            }
            cout<<endl;
        }
    }
    return ;
}
int main()
{
    cin>>M>>N;
    for(int i=0;i<M;i++)
    {
        for(int j=0;j<N;j++)
        {
            cin>>tile[i][j];
        }
    }
    solve();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/wangws_sb/article/details/83097695
今日推荐