POJ1753 棋盤翻轉(位壓縮 廣度優先搜尋)

2021-08-26 19:10:46 字數 2884 閱讀 8341

題目大意:有乙個4*4的方格,每個方格中放一粒棋子,這個棋子一面是白色,一面是黑色。遊戲規則為每次任選16顆中的一顆,把選中的這顆以及它四周的棋子一併反過來,當所有的棋子都是同乙個顏色朝上時,遊戲就完成了。現在給定乙個初始狀態,要求輸出能夠完成遊戲所需翻轉的最小次數,如果初始狀態已經達到要求輸出0。如果不可能完成遊戲,輸出impossible。

主要思想:

1、如果用乙個4*4的陣列儲存每一種狀態,不但儲存空間很大,而且在窮舉狀態時也不方便記錄。因為每一顆棋子都只有兩種狀態,所以可以用二進位制0和1表示每乙個棋子的狀態,則棋盤的狀態就可以用乙個16位的整數唯一標識。而翻轉的操作也可以通過通過位操作來完成。顯然當棋盤狀態id為0(全白)或65535(全黑)時,遊戲結束。

2、對於棋盤的每乙個狀態,都有十六種操作,首先要判斷這十六種操作之後是否有完成的情況,如果沒有,則再對這十六種操作的結果分別再進行上述操作,顯然這裡就要用到佇列來儲存了。而且在翻轉的過程中有可能會回到之前的某種狀態,而這種重複的狀態是不應該再次入隊的,所以維護 visit[i]陣列來判斷 id==i 的狀態之前是否已經出現過,如果不是才將其入隊。如果遊戲無法完成,狀態必定會形成迴圈,由於重複狀態不會再次入隊,所以最後的佇列一定會是空佇列。

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

/*

簡單分析:根據輸入要求,b代表黑棋(black),w代表白棋(white)。因為總共才16個位置,且只有黑白兩種表示,此時,可對每一次狀態進行二進位制壓縮(其中b代表1,w代表0),例如:

bwwb

bbwb

bwwb

bwww

即可表示為1001 1101 1001 1000,其十進位制值為40344。同時,計算可知根據黑白棋的擺放情況,總共有2^16種不同的狀態。每一次經過有效的操作後,狀態都會發生改變,此時,可借助二進位制位運運算實現狀態的改變,即對原有狀態(相應的十進位制表示)進行異或操作,以此來改變其對應二進位制數的相關位置的值(1變0,0變1)。

例如:先假設前乙個狀態為:

wwww

wwww

wwww

wwww

即二進位制表示為0000 0000 0000 0000,十進位制對應為0。若此時選定左上角第乙個棋子進行操作,根據規則,它右邊和下邊的也要同時進行變換(因為其左邊和上邊為空,不做考慮),之後,相應的狀態用二進位制表示,應變為:1100 1000 0000 0000,十進位制值為51200。這個過程相當於對十進位制數51200進行對十進位制數0的異或操作,即next=0^(51200),而51200這個數則可以根據對十進位制數1進行相應的左移操作得到。同時,我們知道,棋牌總共有16個位置,也就是說相應的不同的操作也有16種,即有16個不同的數經過異或操作用來改變前乙個狀態的值。那麼,就先將這16個數枚出來吧,**如下:

*/int dir[4][2]=,,,};

void init()

cout《完整的ac**如下:

#include#include#includeusing namespace std;

#includestruct node

;bool visit[65536];

int change[16] = //16種狀態轉換,對應4*4的翻子位置

;int bfs(int state)

} return -1;

}int main(void)

} ans = bfs(state);

if(ans == -1)

puts("impossible");

else

printf("%d\n",ans);

} return 0;

}

與poj1753相比,這題還要注意翻棋的方法,若不注意會大大浪費時間導致超時,因為是整行整列翻轉,在邊界處會出現很多多餘操作。**中詳細說明。

/*

先看乙個簡單的問題,如何把'+'變成'-'而不改變其他位置上的狀態?答案是將該位置(i,j)及位置所在的行(i)和列(j)上所有的handle更新一次,

結果該位置被更新了7次,相應行(i)和列(j)的handle被更新了6次,剩下的被更新了4次.

被更新偶數次的handle不會造成最終狀態的改變.因此得出高效解法,在每次輸入碰到'+'的時候,

自增該位置與相應的行和列,當輸入結束後,遍歷陣列,所有為奇數的位置則是操作的位置,

而奇數字置的個數之和則是最終的操作次數.

*/#include#includeusing namespace std;

int main(void)

; char handle;

for(i = 0; i < 4; ++i)}}

getchar();

} result = 0;

for(i = 0; i < 4; ++i)

}} printf("%d\n",result);

for(i = 0; i < 4; ++i)

}return 0;

}

方法二: dfs+bit

/***二:dfs+bit

本題由於要輸出每次翻轉的棋子,因此不適宜用bfs,應該使用dfs輸出完整路徑

*/ #include#includeusing namespace std;

int chess; //棋盤狀態

int step;

bool flag=false;

int ri[16],cj[16];

bool isopen(void)

void flip(int bit)

int main(void)

}/*dfs*/

for(step=0;step<=16;step++)

printf("%d\n",step);

for(i=0;i

poj 1753 位壓縮 搜尋

題意 有乙個4 4的方格,每個方格中放一粒棋子,這個棋子一面是白色,一面是黑色。遊戲規則為每次任選16顆中的一顆,把選中的這顆以及它四周的棋子一併反過來,當所有的棋子都是同乙個顏色朝上時,遊戲就完成了。現在給定乙個初始狀態,要求輸出能夠完成遊戲所需翻轉的最小次數,如果初始狀態已經達到要求輸出0。如果...

Poj1753 翻轉棋子

這個題就是用列舉舉遍所有情況,然後乙個乙個深搜看看是不是符合條件,符合條件直接退出,不符合則繼續,由於 只有16個所以可以得知最多的步數只能是16,所以可以根據步數從0到16依次列舉,第乙個符合條件的就是最小的步數,為了容易深搜,可以設定順序為一行一行深搜,當一行搜完時從下一行開頭搜,和測試資料如下...

POJ1753 狀態壓縮( BFS) 棋盤問題

棋盤問題,改變乙個子的顏色,這個子以及這個子周圍的四個位置 左右上下 的子分別變色,求最少的改變次數。此類題大部分應該可以用狀態壓縮 暴力搜尋解決。純粹找規律不太合理。1 第一種方法,狀態壓縮後bfs暴力搜尋。因為棋盤很小,只有16個格仔,列舉所有的狀態共有2 16 65536種。所以有可以用int...