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 時才翻轉當前位置
  • 而對於第一行,則有2N2^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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章