POJ 3279 - Fliptile - [狀壓+暴力枚舉]

題目連接:http://poj.org/problem?id=3279ios

Sample Input數組

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

Sample Outputthis

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

 

題意:spa

給出 $M$ 行 $N$ 的矩陣,每一個元素只爲 $0$ 或者 $1$,分別表明白塊和黑塊,code

每次能夠翻轉一個元素,使得它能夠從黑變白或者從白變黑,可是上下左右相鄰的四個元素也會跟着翻轉,blog

求最少翻轉多少個元素,能夠使得所有元素均變成 $0$。排序

給出須要反轉的元素,如有多個答案,給出字典序最小的,若無答案,輸出"IMPOSSIBLE"。ip

 

題解:ci

首先,顯然的:翻轉只有 $0$ 次和 $1$ 次,多了沒用;其次,翻轉順序無關性,結果與翻轉方塊的順序無關,只與翻轉那幾個方塊有關。get

 

若是從上往下一行一行看,那麼當第 $i$ 行肯定了如何翻轉後,第 $i$ 行上若是剩下來若干個黑塊,那麼只能靠翻轉第 $i+1$ 行來將其變成白塊,

這就註定了:一旦肯定了第 $1$ 行如何翻轉,後面的全部行如何翻轉都被肯定。

因此只要枚舉第 $1$ 行全部翻轉方案便可,列數不超過15,能夠使用狀壓。

 

另外,因爲第一行的翻轉方案一旦給定,後面三行就是固定的,因此翻轉方案的字典序能夠只看第一行,

因此方案能夠存成一個結構體,在存儲方案數組的同時,一併存儲總的翻轉次數,以及,第一行翻轉方案(存成二進制數);在最後進行排序,並輸出第一個便可。

 

AC代碼:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;

const int maxn=18;

int m,n;
int mp[maxn][maxn],tmp[maxn][maxn];

struct Ans{
    int idx;
    int times;
    int f[maxn][maxn];
    Ans()
    {
        this->times=0;
        memset(f,0,sizeof(f));
    }
};
bool cmp(Ans a,Ans b)
{
    if(a.times==b.times) return a.idx<b.idx;
    return a.times<b.times;
}

inline void flip(int i,int j)
{
    if(i>1) tmp[i-1][j]^=1;
    if(i<m) tmp[i+1][j]^=1;
    if(j>1) tmp[i][j-1]^=1;
    if(j<n) tmp[i][j+1]^=1;
    tmp[i][j]^=1;
}

inline bool allwhite()
{
    for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) if(tmp[i][j]) return 0;
    return 1;
}

int main()
{
    cin>>m>>n;
    for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) cin>>mp[i][j];

    vector<Ans> v;
    for(int sta=0;sta<(1<<n);sta++)
    {
        for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) tmp[i][j]=mp[i][j];

        Ans ans;
        for(int j=1;j<=n;j++) if(sta&(1<<(n-j))) flip(1,j), ans.f[1][j]=1, ans.times++;
        for(int i=2;i<=m;i++) for(int j=1;j<=n;j++) if(tmp[i-1][j]) flip(i,j), ans.f[i][j]=1, ans.times++;
        if(allwhite())
        {
            ans.idx=sta;
            v.push_back(ans);
        }
    }
    sort(v.begin(),v.end(),cmp);
    if(v.size()>0) for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) printf("%d%c",(*v.begin()).f[i][j],j<n?' ':'\n');
    else printf("IMPOSSIBLE\n");
}

時間複雜度:$O\left( {2^n mn} \right)$;後面的對全部可行方案的排序,認爲可以達成目標的翻轉方案遠小於 $2^n$,不考慮其時間複雜度)。

相關文章
相關標籤/搜索