題目連接: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$,不考慮其時間複雜度)。