POJ - 3279 Fliptile(DFS,经典翻转问题,二进制枚举)

题目描述

给你一个01矩阵,矩阵大小为M x N。(1 <= M , N <= 15)
每次操作选择一个格子,使得该格子与上下左右四个格子的值翻转。
至少多少次操作可以使得矩阵中所有的值变为0?
请输出翻转方案,若没有方案,输出"IMPOSSIBLE” 。

输入格式

第一行输入两个数:M和N。(1 <= M , N <= 15)
接下来M行,每行N个数,其值只为0或1。

输出格式

输出M行,每行N个数。
每个数代表该位置翻转次数

样例输入

4 4
1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1

样例输出

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






题解:

  • 题目数据较小,可以考虑用DFS搜索
  • 简单的模拟枚举会TLE,考虑简化一下:
  • 如果从 按行往下 开始枚举,则可以让只有当前位置的上一行为 1 时才翻转当前位置
  • 而对于第一行,则有 2 N 2^N 种翻转的可能,对此对于每一种可能进行搜索。最后根据最后一行是否全是0判断成功与否。
  • 对于此类两面翻转问题,判断一个位置的属性可以通过(被翻转次数)% 2 得到。这样就不需要在原数据上模拟翻转了
  • 在该问题中 被翻转次数 = 当前位置的属性(0/1) + 上下左右中 五个位置的翻转次数
  • 模拟第一行的情况可以使用二进制枚举
#include<cstdio>
#include<cstring>
#include<iostream>
//#define DEBUG
using namespace std;

int m, n;
int a[20][20], cnt[20][20], ans=0x3f3f3f3f, sumc = 0, anscnt[20][20];
int dir[5][2] = {{0,0},{0,1},{0,-1},{1,0},{-1,0}};

bool surpass(int i, int j){
    if(i<0 || i>=m || j<0 || j>=n) return true;
    return false;
}

int get(int i, int j)
{
    int temp = a[i][j];
    for(int k=0;k<5;k++)
    {
        int x = i+dir[k][0];
        int y = j+dir[k][1];
        if(!surpass(x, y)) temp += cnt[x][y];
    }
    return temp & 1;
}


void dfs()
{
    for(int i=0;i<(1<<n);i++)
    {
        memset(cnt, 0, sizeof cnt);
        sumc = 0; //改变的次数
        int k = i;
        for(int j=0;j<n;j++){
            cnt[0][n-j-1] = k & 1;
            sumc+= cnt[0][n-j-1];
            k >>= 1;

        }
        for(int j=1;j<m;j++)
        {
            for(k=0;k<n;k++)
            {
                if(get(j-1, k)) //上方为1
                {
                    cnt[j][k]=1;
                    sumc++;
                }
            }
        }
        int sumz = 0;
        for(int j=0;j<n;j++)
        {
            sumz += get(m-1,j); // 统计最后一行的0
        }
        if(sumz==0 && sumc < ans)
        {
            ans = sumc;
            memcpy(anscnt, cnt, sizeof cnt);
        }
    }
}

int main()
{
    scanf("%d %d", &m, &n);
    int sum=0;
    for(int i=0;i<m;i++)
    {
        for(int j=0;j<n;j++){
            scanf("%d", &a[i][j]);
            sum+=a[i][j];
        }
    }
    memset(cnt, 0, sizeof cnt);
    dfs();
    if(ans == 0x3f3f3f3f) cout << "IMPOSSIBLE" << endl;
    else
    for(int i=0;i<m;i++)
    {
        for(int j=0;j<n;j++)
            printf("%d%c", anscnt[i][j], (j==n-1)?'\n':' ');
    }
    return 0;
}

发布了35 篇原创文章 · 获赞 15 · 访问量 1108

猜你喜欢

转载自blog.csdn.net/irimsky/article/details/102538063
今日推荐