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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

POJ1753 棋盘翻转(位压缩+广度优先搜索)

發(fā)布時間:2024/7/19 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 POJ1753 棋盘翻转(位压缩+广度优先搜索) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

http://poj.org/problem?id=1753

題目大意:有一個4*4的方格,每個方格中放一粒棋子,這個棋子一面是白色,一面是黑色。游戲規(guī)則為每次任選16顆中的一顆,把選中的這顆以及它四周的棋子一并反過來,當(dāng)所有的棋子都是同一個顏色朝上時,游戲就完成了。現(xiàn)在給定一個初始狀態(tài),要求輸出能夠完成游戲所需翻轉(zhuǎn)的最小次數(shù),如果初始狀態(tài)已經(jīng)達(dá)到要求輸出0。如果不可能完成游戲,輸出Impossible。

主要思想:

1、如果用一個4*4的數(shù)組存儲每一種狀態(tài),不但存儲空間很大,而且在窮舉狀態(tài)時也不方便記錄。因為每一顆棋子都只有兩種狀態(tài),所以可以用二進(jìn)制0和1表示每一個棋子的狀態(tài),則棋盤的狀態(tài)就可以用一個16位的整數(shù)唯一標(biāo)識。而翻轉(zhuǎn)的操作也可以通過通過位操作來完成。顯然當(dāng)棋盤狀態(tài)id為0(全白)或65535(全黑)時,游戲結(jié)束。

2、對于棋盤的每一個狀態(tài),都有十六種操作,首先要判斷這十六種操作之后是否有完成的情況,如果沒有,則再對這十六種操作的結(jié)果分別再進(jìn)行上述操作,顯然這里就要用到隊列來存儲了。而且在翻轉(zhuǎn)的過程中有可能會回到之前的某種狀態(tài),而這種重復(fù)的狀態(tài)是不應(yīng)該再次入隊的,所以維護(hù) Visit[i]數(shù)組來判斷 id==i 的狀態(tài)之前是否已經(jīng)出現(xiàn)過,如果不是才將其入隊。如果游戲無法完成,狀態(tài)必定會形成循環(huán),由于重復(fù)狀態(tài)不會再次入隊,所以最后的隊列一定會是空隊列。

3、由于0^1=1,1^1=0,所以翻轉(zhuǎn)的操作可以通過異或操作來完成,而翻轉(zhuǎn)的位置可以通過移位來確定。

/* 簡單分析:根據(jù)輸入要求,b代表黑棋(black),w代表白棋(white)。因為總共才16個位置,且只有黑白兩種表示,此時,可對每一次狀態(tài)進(jìn)行二進(jìn)制壓縮(其中b代表1,w代表0),例如: bwwb bbwb bwwb bwww 即可表示為1001 1101 1001 1000,其十進(jìn)制值為40344。同時,計算可知根據(jù)黑白棋的擺放情況,總共有2^16種不同的狀態(tài)。每一次經(jīng)過有效的操作后,狀態(tài)都會發(fā)生改變,此時,可借助二進(jìn)制位運(yùn)運(yùn)算實(shí)現(xiàn)狀態(tài)的改變,即對原有狀態(tài)(相應(yīng)的十進(jìn)制表示)進(jìn)行異或操作,以此來改變其對應(yīng)二進(jìn)制數(shù)的相關(guān)位置的值(1變0,0變1)。 例如: 先假設(shè)前一個狀態(tài)為: wwww wwww wwww wwww 即二進(jìn)制表示為0000 0000 0000 0000,十進(jìn)制對應(yīng)為0。若此時選定左上角第一個棋子進(jìn)行操作,根據(jù)規(guī)則,它右邊和下邊的也要同時進(jìn)行變換(因為其左邊和上邊為空,不做考慮),之后,相應(yīng)的狀態(tài)用二進(jìn)制表示,應(yīng)變?yōu)?#xff1a;1100 1000 0000 0000,十進(jìn)制值為51200。這個過程相當(dāng)于對十進(jìn)制數(shù)51200進(jìn)行對十進(jìn)制數(shù)0的異或操作,即next=0^(51200),而51200這個數(shù)則可以根據(jù)對十進(jìn)制數(shù)1進(jìn)行相應(yīng)的左移操作得到。同時,我們知道,棋牌總共有16個位置,也就是說相應(yīng)的不同的操作也有16種,即有16個不同的數(shù)經(jīng)過異或操作用來改變前一個狀態(tài)的值。那么,就先將這16個數(shù)枚出來吧,代碼如下: */ int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; void init() {int i,j,x,y,t,temp;for(i=0;i<4;++i){for(j=0;j<4;++j){temp = 0;temp ^= (1<<((3-i)*4+3-j)); //第一行代表16位的高4位,同理第一列也代表高位,所以棋盤(i,j)處在16位中的位置是((3-i)*4+3-j)//temp ^= (1<<(15 - ( i*4 + j )));for(t=0;t<4;++t){x = i + dir[t][0];y = j + dir[t][1];if(x<0 || y<0 || x>3 || y>3)continue;temp ^= (1<<((3-x)*4+3-y));}cout<<temp<<" ";}cout<<endl;} }

完整的AC代碼如下:

#include<iostream> #include<queue> #include<cstdio> using namespace std; #include<memory.h>struct Node {int state;int step; };bool visit[65536];int change[16] = //16種狀態(tài)轉(zhuǎn)換,對應(yīng)4*4的翻子位置 {51200,58368,29184,12544,35968,20032,10016,4880,2248,1252,626,305,140,78,39,19 };int bfs(int state) {int i;memset(visit,false,sizeof(visit)); //標(biāo)記每一個狀態(tài)都未訪問過queue<Node>q;Node cur,next;cur.state = state;cur.step = 0;q.push(cur);visit[state] = true;while(!q.empty()){cur = q.front();q.pop();if(cur.state == 0 || cur.state == 0xffff) //65535return cur.step;for(i=0;i<16;i++){next.state = cur.state^change[i];next.step = cur.step + 1;if(visit[next.state])continue;if(next.state == 0 || next.state == 0xffff) //65535return next.step;visit[next.state] = true;q.push(next);}}return -1; }int main(void) {int i,j,state,ans;char ch[5][5];while(scanf("%s",ch[0])!=EOF){for(i = 1 ; i < 4 ; ++i)scanf("%s",ch[i]);state = 0;for(i = 0 ; i < 4 ; ++i){for(j = 0 ; j < 4 ; ++j){state <<= 1;if(ch[i][j] == 'b')state += 1;//state ^= (1<<((3-i)*4+(3-j)));}}ans = bfs(state);if(ans == -1)puts("Impossible");elseprintf("%d\n",ans);}return 0; } POJ? 2965-The Pilots Brothers' refrigerator
提示:這題和POJ1753翻轉(zhuǎn)棋的思想是一致的,需要注意的是要求輸出翻轉(zhuǎn)過程,因此不能用BFS,必須用DFS(找到目標(biāo)后,還要過程回溯)

與POJ1753相比,這題還要注意翻棋的方法,若不注意會大大浪費(fèi)時間導(dǎo)致超時,因為是整行整列翻轉(zhuǎn),在邊界處會出現(xiàn)很多多余操作。代碼中詳細(xì)說明

/* 先看一個簡單的問題,如何把'+'變成'-'而不改變其他位置上的狀態(tài)?答案是將該位置(i,j)及位置所在的行(i)和列(j)上所有的handle更新一次, 結(jié)果該位置被更新了7次,相應(yīng)行(i)和列(j)的handle被更新了6次,剩下的被更新了4次. 被更新偶數(shù)次的handle不會造成最終狀態(tài)的改變.因此得出高效解法,在每次輸入碰到'+'的時候, 自增該位置與相應(yīng)的行和列,當(dāng)輸入結(jié)束后,遍歷數(shù)組,所有為奇數(shù)的位置則是操作的位置, 而奇數(shù)位置的個數(shù)之和則是最終的操作次數(shù). */ #include<iostream> #include<cstdio> using namespace std;int main(void){int i,j,k,result;bool ch[4][4] = {false};char handle;for(i = 0; i < 4; ++i){for(j = 0; j < 4; ++j){scanf("%c",&handle);if(handle == '+'){ch[i][j] = !ch[i][j];for(k = 0; k < 4; ++k){ch[i][k] = !ch[i][k];ch[k][j] = !ch[k][j];}}}getchar();}result = 0;for(i = 0; i < 4; ++i){for(j = 0; j < 4; ++j){if(ch[i][j]){++result;}}}printf("%d\n",result);for(i = 0; i < 4; ++i){for(j = 0; j < 4; ++j){if(ch[i][j])printf("%d %d\n",i+1,j+1);}}return 0;}

方法二: DFS+Bit

/* 代碼二:DFS+Bit 本題由于要輸出每次翻轉(zhuǎn)的棋子,因此不適宜用BFS,應(yīng)該使用DFS輸出完整路徑 */#include<iostream>#include<cstdio>using namespace std;int chess; //棋盤狀態(tài)int step;bool flag=false;int ri[16],cj[16];bool isopen(void){if(chess == 0xFFFF)return true;elsereturn false;}void flip(int bit){chess=chess^(0x1<<bit); //對翻轉(zhuǎn)位取反int row=bit/4;int col=bit%4;for(int c=0;c<4;c++)chess=chess^(0x1<<(row*4+c)); //對全行取反for(int r=0;r<4;r++)chess=chess^(0x1<<(r*4+col)); //對全列取反return;}void dfs(int bit,int deep){if(deep==step){flag=isopen();return;}if(flag || bit>15)return;int row=ri[deep]=bit/4;int col=cj[deep]=bit%4;flip(bit);if(col<4)dfs(bit+1,deep+1);elsedfs((bit+4)/4*4,deep+1);flip(bit);if(col<4)dfs(bit+1,deep);elsedfs((bit+4)/4*4,deep);return;}int main(void){char temp;int i,j;for(i=0;i<4;i++){for(j=0;j<4;j++){cin>>temp;if(temp=='-')chess=chess^(1<<(i*4+j));}}/*DFS*/for(step=0;step<=16;step++){dfs(0,0);if(flag)break;}printf("%d\n",step);for(i=0;i<step;i++)printf("%d %d\n",ri[i]+1,cj[i]+1);return 0;}



?

總結(jié)

以上是生活随笔為你收集整理的POJ1753 棋盘翻转(位压缩+广度优先搜索)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。