POJ - 3279 Fliptile(枚举+位运算+视频讲解)

题目描述

英文

Description
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”.
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
Output
Lines 1…M: Each line contains N space-separated integers, each specifying how many times to flip that particular location.

样例1

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

中文

给你一个n*m的矩阵,问将那些点进行多少次操作可以将矩阵全部变为0。每次翻转一个格子与它上下左右相邻的格子也会被翻转。
输出最小的次数,次数相同输出字典序最小的一个,否则输出IMPOSSIBLE,输出的也是一个矩形,0表示此位置不翻转,其他数表示此位置上翻转几次。
分析
如何把1变成0?
翻转自身—>(不行):因为翻转会影响周围的格子,
那么:翻转1下方的棋子,使其变成0,一直到n-1行,特判最后一行。

提交链接

http://poj.org/problem?id=3279

解析

定义temp数组,表示翻转的状态,0表示未翻转,1表示翻转一次,同一个格子翻转两次的话,会被恢复原状,可见多余的翻转是多余的。
需要注意的是输入的mp数组中的0和1,是表示黑白颜色,而temp数组中的0和1表示的是是否翻转。

判断一个点是否需要翻转,可对自身加上4个方向进行判断。

    int color(int x,int y) //看这个点是否需要翻转
    {
        int res=mp[x][y];
        for(int i=0; i<5; i++)
        {
            int tx=x+to[i][0];
            int ty=y+to[i][1];
            if(tx<0||tx>=n||ty<0||ty>=m)
                continue;
            res+=temp[tx][ty];
        }
        return res%2;//翻转偶数次会被恢复原状
    }

枚举第一行的所有翻转情况

    for(int i=0; i<(1<<m); i++)
      memset(temp,0,sizeof(temp));
            for(int j=0; j<m; j++)
                temp[0][j]=(i>>j)&1;

然后从第二行开始遍历,看每个点是否需要翻转,最后特判最后一行,若最后一行全部为0,就说明这种翻转情况可行,再维护最小的翻转次数就ok了。

参考代码:

 	#include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define N 20
    #define INF 0x3f3f3f3f
    int n,m,mp[N][N];
    int temp[N][N],a[N][N];
    int to[5][2]= {0,0,1,0,0,-1,-1,0,0,1};
    int color(int x,int y) //看这个点是否需要翻转
    {
        int res=mp[x][y];/*先看自身的情况,再加上相邻点的翻转情况*/
        for(int i=0; i<5; i++)
        {
            int tx=x+to[i][0];
            int ty=y+to[i][1];
            if(tx<0||tx>=n||ty<0||ty>=m)
                continue;
            res+=temp[tx][ty];
        }
        return res%2;
    }
    int solve()
    {
        for(int i=1; i<n; i++)
        {
            for(int j=0; j<m; j++)
            {
                if(color(i-1,j))
                    temp[i][j]=1;
            }
        }
        for(int i=0; i<m; i++)//特判最后一行
            if(color(n-1,i))
                return INF;
        int cut=0;
        for(int i=0; i<n; i++)
            for(int j=0; j<m; j++)
                cut+=temp[i][j];
        return cut;
    }
    int main()
    {
        int result=INF;
        scanf("%d%d",&n,&m);
        for(int i=0; i<n; i++)/*输入地图*/
            for(int j=0; j<m; j++)
                scanf("%d",&mp[i][j]);
        //枚举第一行的状态
        for(int i=0; i<(1<<m); i++)/*每个点只有两种状态:翻转和不翻转,总共有2的m次方中可能性*/
        {
            memset(temp,0,sizeof(temp));
            for(int j=0; j<m; j++)
                temp[0][j]=(i>>j)&1;
            //printf("%d",temp[0][j]);//printf("\n");
            int sum=solve();//翻转了几次
            if(sum<result)
            {
                result=sum;
                memcpy(a,temp,sizeof(temp));/*c++中的拷贝函数,将temp中sizeof(temp)个字节拷贝到a中*/
    
            }
        }
        if(result==INF)
            printf("IMPOSSIBLE\n");
        else
        {
            for(int i=0; i<n; i++)
            {
                for(int j=0; j<m; j++)
                {
                    if(j==0)
                        printf("%d",a[i][j]);
                    else
                        printf(" %d",a[i][j]);
                }
                printf("\n");
            }
        }
        return 0;
    }

视频

POJ - 3279 Fliptile

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zaiyang遇见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值