Fliptile POJ - 3279 (區域性列舉 + 狀態壓縮)
阿新 • • 發佈:2018-11-09
https://vjudge.net/problem/POJ-3279
參考部落格: https://blog.csdn.net/loy_184548/article/details/50949972
這道題目在vj上的kuangbin比賽中被歸類到了搜尋, 其實它還算不上搜素, 是一道比較基礎的列舉題目,和熄燈問題, 畫家問題,撥鍾問題是一類題, 如果單純用爆搜或者列舉來考慮的話可能會超時,複雜度O(2^N*M) 需要一點點小的技巧
首先, 我們來考慮一下, 改變當前一行的只有按下當前一行或者是上一行正對著的按鈕.
所以, 我們只用考慮第一行, 列舉第一行的開關操作, 然後與之對應的第二行就確定了, 第二行又確定了第三行...以此類推, 直到最後一行. 我們來判斷是否全部歸0
需要注意的是題中要求輸出最優解, 多個最優解又要求以字典序最小的輸出, 我在此用了一個字典序的狀態壓縮, 可以直接拿走下次用.
//Flip Tile 列舉 #include <stdio.h> #include<cstring> const int maxn = 20; int M, N; int color[maxn][maxn], cur[maxn][maxn];//題中讀入顏色, 當前操作顏色(防止直接改變源顏色) int oper[maxn][maxn], ans[maxn][maxn], steps = 0, minSteps = 1<<30; //當前操作, 最小操作, 當前解和最小解 void press(int x, int y) { //按下x, y處的按鈕 cur[x][y]^=1, cur[x+1][y]^=1, cur[x-1][y]^=1, cur[x][y+1]^=1, cur[x][y-1]^=1; } bool solve() { //判斷是否已經解決問題 memcpy(cur, color, sizeof(color)); //根據列舉結果改變第一二行 for(int i = 1; i <= N; i++) if(oper[1][i]) press(1, i), steps++; //根據第i-1行決定第i行的操作 for(int i = 2; i <= M; i++){ for(int j = 1; j <= N; j++) if(cur[i-1][j]) oper[i][j]=1, press(i, j), steps++; } //判斷最後一行是否滿足條件 for(int i = 1; i <= N; i++) if(cur[M][i]) return 0; return 1; } int main() { scanf("%d%d",&M,&N); for(int i = 1; i <= M; i++) for(int j = 1; j <= N; j++) scanf("%d",&color[i][j]); //僅僅列舉第一行的狀態即可 for(int i = 0; i < (1<<N); i++){ //狀態壓縮 memset(oper, 0, sizeof(oper)), steps = 0; //初始化不要忘 for(int j = 0; j < N; j++){ oper[1][N-j] = (i>>j&1); } if(solve() && steps>0 && steps<minSteps) minSteps = steps, memcpy(ans, oper, sizeof(oper));; } if(minSteps < (1<<30)) for(int i = 1; i <= M; i++){ for(int j = 1; j <= N; j++) printf("%d ",ans[i][j]); printf("\n"); } else printf("IMPOSSIBLE\n"); return 0; }