日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Fliptile (二进制压缩)

發布時間:2024/4/15 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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 (二进制压缩)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。