Fliptile (二进制压缩)
題目鏈接:http://poj.org/problem?id=3279
?
題目大意:有一個n*m的棋盤,0表示白色,1表示黑色。每次可以翻轉當前位置,它的上下左右四個位置也會被相應翻轉。問最少翻轉多少次會使所有棋面顯示為白色,并給出需要翻轉的位置,0表示不翻轉,1表示翻轉。
?
思路:第一行的翻轉狀態決定了你第二行的翻轉狀態。也可以說第n-1行的翻轉狀態決定了第n行應該如何去翻轉。? ? 所以我們可以說第一行起到了決定性的作用
所以我們去枚舉第一行的所有的可能的情況。
?
這道題比較巧妙的地方就是它利用了二進制壓縮,更加方便的去枚舉了第一行的每種情況。
?
首先讓我們來學習一下左移<<的概念,其實很好理解,就是將一個數轉換為二進制,然后向左移動若干位,然后在多出來的位置上補零,比如,對于5<<2,5的二進制為101,左移兩位就是10100,那么最后的結果就是20,。?
利用這個特點,我們可以通過使用一個特殊的數字1,來解決這個枚舉問題。枚舉的第一步就是確定到底要改變哪幾位數,聯想到二進制,我們可以這樣處理,就題目的測試數據而言,一行有四個數,用一個二進制數xxxx表示,易知,xxxx一共有2^4種排列,其實也是1<<4,之所以這樣寫,是因為這比pow要快,所以,我們讓k從0開始枚舉到15,也就是0000到1111,然后規定,只要是帶1的位置,就要翻轉這個位置的數字,問題又來了,怎么知道哪一位是1,呢,這里還是用到了二進制,即與運算,我們讓k分別與1000,0100,0010,0001進行與運算,分別對應不同的位,如果結果不是1,說明這一位上不是0,是不是灰常巧妙,當然,那四個值依然是通過1的左移來計算出來的
具體代碼:
?
1 #include <iostream> 2 #include <string> 3 #include <cstring> 4 using namespace std; 5 6 int Map[20][20],cal[20][20],out[20][20]; 7 int n,m; 8 int dir[5][2] = {{0,0},{0,1},{0,-1},{1,0},{-1,0}}; 9 10 int fuc(int x,int y){ //(x,y)的狀態由本身的黑白 + 周圍五個的翻轉狀態決定 11 int temp = Map[x][y]; 12 13 for(int i = 0;i < 5;i ++){ 14 int xi = x+dir[i][0]; 15 int yi = y+dir[i][1]; 16 17 if(xi < 1 || xi > n || yi < 1 || yi > m) continue; 18 temp += cal[xi][yi]; 19 } 20 return temp%2; 21 } 22 int dfs(){ 23 for(int i = 2;i <= n;i ++) 24 for(int j = 1;j <= m;j ++) 25 if(fuc(i-1,j)) //如果上方為黑色,必須要翻轉 26 cal[i][j] = 1; 27 28 for(int i = 1;i <= m;i ++) //最后一行全白 29 if(fuc(n,i)) 30 return -1; 31 32 int res = 0; 33 for(int i = 1;i <= n;i ++) 34 for(int j = 1;j <= m;j ++) 35 res += cal[i][j]; 36 return res; 37 } 38 39 int main() 40 { 41 while(cin>>n>>m){ 42 for(int i = 1;i <= n;i ++) 43 for(int j = 1;j <= m;j ++) 44 cin>>Map[i][j]; 45 46 int flag = 0; 47 int ans = 0x3f3f3f3f; 48 for(int i = 0;i < 1<<m;i ++){ //第一行 1<<m種狀態,二進制從0開始,字典序從小到大 49 memset(cal,0,sizeof(cal)); 50 51 for(int j = 1;j <= m;j ++) //利用二進制枚舉第一行所有的情況 52 cal[1][m-j+1] = i>>(j-1) & 1; // cal數組存貯的就是翻轉的情況了 53 int cont = dfs(); 54 if(cont >= 0 && cont < ans){ //翻轉次數最少 55 flag = 1; 56 ans = cont; 57 memcpy(out,cal,sizeof(cal)); 58 } 59 } 60 if(!flag) cout<<"IMPOSSIBLE"<<endl; 61 else{ 62 for(int i = 1;i <= n;i ++){ 63 for(int j = 1;j <= m;j ++){ 64 if(j != 1) cout<<" "; 65 cout<<out[i][j]; 66 } 67 cout<<endl; 68 } 69 } 70 } 71 return 0; 72 }?
轉載于:https://www.cnblogs.com/-Ackerman/p/11186485.html
總結
以上是生活随笔為你收集整理的Fliptile (二进制压缩)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Git.之.最小化配置
- 下一篇: poj1426_模拟BFS