POJ 3279-Fliptile(母牛翻方格-開關問題)

kewlgrl發表於2016-06-15

Fliptile
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 6893   Accepted: 2616

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.

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

Source


題目意思:

農夫約翰知道聰明的牛產奶多。於是為了提高牛的智商他準備瞭如下游戲。有一個M×N 的格子,每個格子可以翻轉正反面,它們一面是黑色,另一面是白色。黑色的格子翻轉後就是白色,白色的格子翻轉過來則是黑色。遊戲要做的就是把所有的格子都翻轉成白色。不過因為牛蹄很大,所以每次翻轉一個格子時,與它上下左右相鄰接的格子也會被翻轉。因為翻格子太麻煩了,所以牛都想通過儘可能少的次數把所有格子都翻成白色。現在給定了每個格子的顏色,請求出用最小步數完成時每個格子翻轉的次數。最小步數的解有多個時,輸出字典序最小的一組。解不存在的話,則輸出IMPOSSIBLE。


解題思路:

首先,同一個格子翻轉兩次的話就會恢復原狀,所以多次翻轉是多餘的。此外,翻轉的格子的集合相同的話,其次序是無關緊要的。因此,總共有2NM種翻轉的方法。不過這個解空間太大了,我們需要想出更有效的辦法。讓我們再回顧一下前面的問題。在那道題中,讓最左端的牛反轉的方法只有1種,於是用直接判斷的方法確定就可以了。同樣的方法在這裡還行得通嗎?不妨先看看最左上角的格子。在這裡,除了翻轉(1,1)之外,翻轉(1,2)和(2,1)也可以把這個格子翻轉,所以像之前那樣直接確定的辦法行不通。於是不妨先指定好最上面一行的翻轉方法。此時能夠翻轉(1,1)的只剩下(2,1)了,所以可以直接判斷(2,1)是否需要翻轉。類似地(2,1)~(2,N)都能這樣判斷,如此反覆下去就可以確定所有格子的翻轉方法。最後(M,1)~(M,N)如果並非全為白色,就意味著不存在可行的操作方法。像這樣,先確定第一行的翻轉方式,然後可以很容易判斷這樣是否存在解以及解的最小步數是多
少,這樣將第一行的所有翻轉方式都嘗試一次就能求出整個問題的最小步數。這個演算法中最上面一行的翻轉方式共有2N種,複雜度為O(MN2N)。



#include<iostream>
#include<cstring>
#include<cstdio>
#include<malloc.h>
using namespace std;

const int MAXN=16;
const int inf=0x3f3f3f3f;
const int dx[5]= {-1,0,0,0,1};//鄰接格子的方向
const int dy[5]= {0,-1,0,1,0};

int M,N;//長寬
int tile[MAXN][MAXN];//初始狀態
int flip[MAXN][MAXN];//中間結果翻轉次數
int opt[MAXN][MAXN];//最優解

int get(int x,int y)//查詢x,y的顏色
{
    int c=tile[x][y];
    for(int i=0; i<5; ++i)
    {
        int x2=x+dx[i];
        int y2=y+dy[i];
        if(x2>=0&&x2<M&&y2>=0&&y2<N)
            c+=flip[x2][y2];
    }
    return c&1;//奇數為1,偶數為0
}
//求出第一行確定情況下的最小操作次數
//不存在解的話輸出-1
int calc()
{
    //求出第2行開始的翻轉方法
    for(int i=1; i<M; ++i)
        for(int j=0; j<N; ++j)
            if(get(i-1,j)!=0)//0白1黑,是黑色則翻轉
                flip[i][j]=1;
    //判斷最後一行是否全白
    for(int j=0; j<N; ++j)
        if(get(M-1,j)!=0)
            return -1;
    int res=0;//統計翻轉次數
    for(int i=0; i<M; ++i)
        for(int j=0; j<N; ++j)
            res+=flip[i][j];
    return res;
}
void solve()
{
    int res=-1;
    for(int i=0; i< (1<<N); ++i) //按字典序嘗試第一行的所有可能性
    {
        memset(flip,0,sizeof(flip));
        for(int j=0; j<N; j++)
            flip[0][N-j-1] = i>>j&1;
        int num=calc();//計算反轉次數
        if(num>=0&&(num<res||res<0))
        {
            res=num;
            memcpy(opt,flip,sizeof(flip));//更新最優解
        }
    }
    if(res<0) puts("IMPOSSIBLE");
    else
        for(int i=0; i<M; ++i)
            for(int j=0; j<N; ++j)
                printf("%d%c",opt[i][j],j+1==N?'\n':' ');
}
int main()
{
    while(cin>>M>>N)
    {
        for(int i=0; i<M; ++i)
            for(int j=0; j<N; ++j)
                scanf("%d",&tile[i][j]);
        solve();
    }
    return 0;
}


相關文章