zoukankan      html  css  js  c++  java
  • 【POJ-3279】Fliptile

    Farmer John knows that an intellectually satisfied cow is a happy cow who will give more milk. He has arranged a brainy activity for cows in which they manipulate an M × N grid (1 ≤ M ≤ 15; 1 ≤ N ≤ 15) of square tiles, each of which is colored black on one side and white on the other side.

    As one would guess, when a single white tile is flipped, it changes to black; when a single black tile is flipped, it changes to white. The cows are rewarded when they flip the tiles so that each tile has the white side face up. However, the cows have rather large hooves and when they try to flip a certain tile, they also flip all the adjacent tiles (tiles that share a full edge with the flipped tile). Since the flips are tiring, the cows want to minimize the number of flips they have to make.

    Help the cows determine the minimum number of flips required, and the locations to flip to achieve that minimum. If there are multiple ways to achieve the task with the minimum amount of flips, return the one with the least lexicographical ordering in the output when considered as a string. If the task is impossible, print one line with the word "IMPOSSIBLE".

    (农夫约翰知道,一头知识上满意的母牛是能多产牛奶的快乐母牛。他为母牛安排了脑筋活泼的活动,他们操纵M×N格子(1≤M≤15; 1≤N≤15)的正方形砖,每块砖的一侧为黑色,另一侧为白色。 就像人们猜测的那样,当翻转单个白色图块时,它会变为黑色。翻转单个黑色瓷砖时,它将变为白色。母牛在翻转砖块时会得到奖励,所以需要将每块砖块的白色侧面都朝上。但是,奶牛的蹄子相当大,当他们尝试翻转某个瓷砖时,它们也会翻转所有相邻的瓷砖(与翻转后的瓷砖共享完整边缘的瓷砖)。由于翻转很累,奶牛希望尽量减少翻转次数。 帮助奶牛确定所需的最小翻转次数,以及达到该最小翻转的位置。如果有多种方法可以以最小的翻转次数来完成任务,则当将其视为字符串时,以输出中词典顺序最少的方式返回。如果无法完成任务,请用“ IMPOSSIBLE”一词打印一行。)

    Input

    Line 1: Two space-separated integers: M and N
    Lines 2.. M+1: Line i+1 describes the colors (left to right) of row i of the grid with N space-separated integers which are 1 for black and 0 for white

    (第1行:两个以空格分隔的整数:M和N

    第2行。M+ 1:第i + 1行用N个以空格分隔的整数描述网格第i行的颜色(从左到右),黑色为1,白色为0)

     

    Output

    Lines 1.. M: Each line contains N space-separated integers, each specifying how many times to flip that particular location.

    行1 .. M:每行包含N个以空格分隔的整数,每个整数指定翻转该特定位置的次数。

    Sample Input
    4 4
    1 0 0 1
    0 1 1 0
    0 1 1 0
    1 0 0 1
    Sample Output
    0 0 0 0
    1 0 0 1
    1 0 0 1
    0 0 0 0

    这是kuangbin专题一的第四题,这题主要的考察的就是思维还有状态压缩
    解题的关键就是需要知道除第一行外的每一行都会受上一行的决策影响
    那么我们只需要枚举第一行的情况,之后一行行turn下来,看看最后一行是不是都是白色就好了。

    关于枚举第一行就需要用到状态压缩了。(对于一个棋子来说,翻转两次后又回到了它原本的状态,所以对一个棋子来说,翻转偶数次是没有意义的。)
    比如第一行有三个棋子,我们用0代表不翻转,1代表翻转。
    那么我们用 000 ,001, 010,100,101,110,111  这六个序列表示第一行所有可能的翻转状态。
    把这些序列看做二进制数的话,那么就可以转换为,0,1,2,3,4,5,6

    那我们可以利用位运算,用6以内的数来表示所有可能的状态。
    下面一份代码是我认为网上注释比较好的一份代码:
    #include<iostream>
    #include<cstring>
    using namespace std;
    
    const int maxn=17;
    ///描述某个点周围的四个点和它本身
    int dir[5][2]= {{0,0},{0,1},{1,0},{0,-1},{-1,0}};
    ///Map存放棋盘,turn存放每个棋子(地板)翻转的状态(翻转几次)
    ///ans存放最后要输出的每个地板翻转的次数,cnt记录翻转地板的总次数
    ///res存放最小的翻转次数,M代表行数,N代表列数
    int Map[maxn][maxn],turn[maxn][maxn],ans[maxn][maxn],cnt,res,M,N;
    
    ///获取当前坐标地板的颜色,0--白色,1--黑色
    ///这儿还有一种方法,不写getcolor,再定义一个cur[maxn][maxn]来保存当前的地板颜色
    ///getcolor实际上是通过turn来推断地板的颜色
    int getColor(int x,int y)
    {
        int temp=Map[x][y];
        for(int i=0; i<5; i++)
        {
            int xi=x+dir[i][0];
            int yi=y+dir[i][1];
            ///因为(x,y)受到周围地板及自己翻转的影响
            if(xi>=0 && xi<M && yi>=0 && yi<N)  temp+=turn[xi][yi];
        }
        return temp%2;
    }
    
    ///从第二层一直搜索到第M层
    void check()
    {
        for(int i=1; i<M; i++)
        {
            for(int j=0; j<N; j++)
            {
                if(getColor(i-1,j)) turn[i][j]=1,cnt++;  ///turn[i][j]=1,1表示要翻转,0不翻转
                if(cnt>res) return;  ///这里可以进行一点优化(剪枝)
            }
        }
        ///下面检测最后一层是否都是白色
        for(int i=0; i<N; i++)
            if(getColor(M-1,i)) return;
        ///更新ans
        if(cnt<res) memcpy(ans,turn,sizeof(turn)),res=cnt;
    }
    
    int main()
    {
        while(cin>>M>>N) ///不写循环也可以,系统既可以单例也可以多例测试
        {
            res=0x3f3f3f3f;
            for(int i=0; i<M; i++)
                for(int j=0; j<N; j++)
                    cin>>Map[i][j];
            ///利用二进制位移来枚举第一行翻转的所有情况,
            for(int i=0; i< 1<<N; i++) ///i<2^N
            {
                cnt=0;
                memset(turn,0,sizeof(turn));
                for(int j=0; j<N; j++)
                {
                    turn[0][N-1-j]=i>>j&1;  ///从后往前填,符合字典序从小到大
                    if(turn[0][N-1-j]) cnt++;
                ///但是从前往后填,即把N-1-j替换为j,也可以AC,故我怀疑测试数据中没有考虑字典序的用例
                }
                check();
            }
            if (res == 0x3f3f3f3f) 
                cout << "IMPOSSIBLE" << endl;
            else
            {
                for (int i = 0; i < M; i++)
                {
                    for (int j = 0; j < N; j++)
                    {
                        if (j > 0) cout << ' ';
                        cout << ans[i][j];
                    }
                    cout << endl;
                }
            }
        }
        return 0;
    }
    
    
    



  • 相关阅读:
    反射学习(三)--------利用反射调用方法
    list实体数据分组
    利用dockerfile制作基于centos7的lnmp镜像(亲测,详细版)
    基于数组的shell脚本编写
    Ansible中文权威指南
    几种方法来实现scp拷贝时无需输入密码
    Java 中&&和&的区别
    常用javaScript小常识
    mysql数据库函数
    Hidden的应用
  • 原文地址:https://www.cnblogs.com/Vikyanite/p/12187106.html
Copyright © 2011-2022 走看看