[POJ2965]The Pilots Brothers' refrigerator (搜尋/位運算)
阿新 • • 發佈:2018-12-15
題意
遊戲“The Pilots Brothers:跟隨有條紋的大象”有一個玩家需要開啟冰箱的任務。
冰箱門上有16個把手。每個手柄可以處於以下兩種狀態之一:開啟或關閉。只有當所有把手都開啟時,冰箱才會開啟。手柄表示為矩陣4х4。您可以在任何位置[i,j](1≤i,j≤4)更改控制代碼的狀態。但是,這也會更改第i行中所有控制代碼的狀態以及第j列中的所有控制代碼。
任務是確定開啟冰箱所需的最小手柄切換次數。
思路
一個和“費解的開關”,"棋盤翻轉",這樣的位運算的題目很像,只不過這次一次翻轉1行+1列
其實可以用1個數就可以記錄狀態,但是我懶……
(其實是不會狀壓啦)
Code
#include<cstdio> using namespace std; #define max(a,b) (a>b?a:b) #define min(a,b) (a<b?a:b) int x[20], y[20]; int map[6][6]; int ans=33; int ansX[20],ansY[20]; int read() { char ch = getchar(); while (ch!='-' && ch!='+') ch = getchar(); return ch=='-'?1:0; } void build() { for(int i=0;i<4;i++) for (int j = 0; j < 4; j++) map[i][j] = read(); } void flip(int s) { int x1 = s / 4; int y1 = s % 4; for (int i = 0; i < 4; i++) { map[i][y1] ^= 1; map[x1][i] ^= 1; } map[x1][y1]^= 1; } bool check() { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) if (!map[i][j]) return 0; } return 1; } void dfs(int s, int b) { if (check()) { if (ans > b) { ans = b; for (int i = 1; i <= ans; i++) ansX[i] = x[i], ansY[i] = y[i]; } return; } if (s >= 16) return; dfs(s + 1, b); flip(s); x[b + 1] = s / 4 + 1; y[b + 1] = s % 4 + 1; dfs(s + 1, b + 1); flip(s); return; } int main() { build(); dfs(0, 0); printf("%d\n", ans); for (int i = 1; i <= ans; i++) printf("%d %d\n", ansX[i], ansY[i]); return 0; }
其他做法
部落格上搜到的
放出來
註釋也很詳細了
用奇偶性做的
/* 參考高手的高效解法: > 證明:要使一個為'+'的符號變為'-',必須其相應的行和列的運算元為奇數;可以證明,如果'+'位置對應的行和列上每一個位置都進行一次操作,則整個圖只有這一'+'位置的符號改變,其餘都不會改變. > 設定一個4*4的整型陣列,初值為零,用於記錄每個點的運算元,那麼在每個'+'上的行和列的的位置都加1,得到結果模2(因為一個點進行偶數次操作的效果和沒進行操作一樣,這就是樓上說的取反的原理),然後計算整型陣列中一的 > 個數即為運算元,一的位置為要操作的位置(其他原來運算元為偶數的因為操作並不發生效果,因此不進行操作) ********************************* 此上證其可以按以上步驟使陣列中值都為‘-’ ******************************** 在上述證明中將所有的行和列的位置都加1後,在將其模2之前,對給定的陣列狀態,將所有的位置操作其所存的運算元個次數,舉例,如果a[i][j]==n,則對(i,j)操作n次,當所有的操作完後,即全為‘-’的陣列。 其實就是不模2的操作,作了許多的無用功。 以上的操作次序對結果無影響,如果存在一個最小的步驟,則此步驟一定在以上操作之中。(簡單說下:因為以上操作已經包含了所有可改變欲改變位置的操作了) 而模2後的操作是去掉了所有無用功之後的操作,此操作同樣包含最小步驟。 但模2後的操作去掉任何一個或幾個步驟後,都不可能再得到全為‘-’的。(此同樣可證明:因為操作次序無影響,先進行最小步驟,得到全為‘-’,如果還剩下m步,則在全為‘-’的陣列狀態下進行這m步操作後還得到一個全為 ‘-’的陣列狀態,此只能是在同一個位置進行偶數次操作,與前文模2後矛盾,所以m=0),因此模2後的操作即為最小步驟的操作。 */ #include <iostream> using namespace std; bool mark[4][4]; char s[4][4]; int main() { int i,j,k; int ci[16],cj[16]; int nas = 0; memset(mark,0,sizeof(mark)); for(i = 0;i < 4;i++) cin >> s[i]; for(i = 0;i < 4;i++) for(j = 0;j < 4;j++) { char c = s[i][j]; if(c == '+') { mark[i][j] = !mark[i][j]; for(k = 0;k < 4;k++) { mark[i][k] = !mark[i][k]; mark[k][j] = !mark[k][j]; } } } for(i = 0;i < 4;i++) for(j = 0;j < 4;j++) if(mark[i][j] == true) { ci[nas] = i + 1; cj[nas] = j + 1; nas ++; } printf("%d\n",nas); for(i = 0;i < nas;i++) { printf("%d %d\n",ci[i],cj[i]); } return 0; }