在一個4*4的棋盤上有8個黑棋和8個白棋,當且僅當兩個格子有公共邊,這兩個格子上的棋是相鄰的。移動棋子的規則是交換相鄰兩個棋子。如今給出一個初始棋盤和一個最終棋盤,要求你找出一個最短的移動序列使初始棋盤變爲最終棋盤。
Klux說:「這麼簡單的題目,我都會作!」ios
第1到4行每行四個數字(1或者0),描述了初始棋盤
接着是一個空行
第6到9行每行四個數字,描述了最終棋盤框架
輸出只有一行是一個整數n,表示最少的移動步數。spa
輸入樣例#1:
1111
0000
1110
0010code
1010
0101
1010
0101ci
輸出樣例#1:io
4stream
BFS+位運算。
因爲要求最小步數能夠看出BFS的基本框架,可是若是用矩陣存儲狀態的話太耗費空間並且很慢,注意到每一個格子的狀態非0即1並且總格子數目爲16因此能夠用二進制的方法存儲狀態,相應判斷,轉移,判重。
注意這裏面將棋盤轉換成二進制序列的時候,如何計算序列上的值在原4*4棋盤上的位置,以及使用異或運算去生成相鄰格子交換後的新棋盤狀態對應的二進制序列也是本題特點。
最後,因爲是交換相鄰的格子,理論上格子和上下左右四個方向均可以互換,可是顯然對於每一個格子這樣枚舉互換存在大量的重複,本質上對於每一個格子從上至下、從左到右只須要讓他往右和往下和相鄰的格子嘗試互換便可。二進制
#include<iostream> #include<queue> #define FOR(a,b,c) for(int a=(b);a<(c);a++) using namespace std; const int maxn = 16; struct Node{ // 結構體存棋盤的二進制序列和步數 int num,d; }; int A,B; int vis[100000]; void BFS() { queue<Node> q; q.push((Node){A,0}); while(!q.empty()) { Node u=q.front(); q.pop(); int tmp=u.num; if(tmp==B) { cout<<u.d; return ; } for(int i=15;i>=0;i--) // 棋盤對應的二進制序列,從高到低依次枚舉每一個位置 { int x=(15-i)/4,y=(15-i)%4,w=1<<i,z; //計算該位置在棋盤上的x和y座標值 if(y<3 && (tmp&(1<<i))!=(tmp&(1<<i-1))) //向右交換,二進制序列的i和i-1交換 { z=1<<i-1; if(!vis[tmp^z^w]) { vis[tmp^z^w]=1; q.push((Node){tmp^z^w,u.d+1}); } } if(x<3 && (tmp&(1<<i))!=(tmp&(1<<i-4))) //向下交換,二進制序列的i和i-4交換 { z=1<<i-4; if(!vis[tmp^z^w]) { vis[tmp^z^w]=1; q.push((Node){tmp^z^w,u.d+1}); } } } } } int main() { char c; for(int i=15;i>=0;i--) { cin>>c; if(c!='0') A += 1<<i; } for(int i=15;i>=0;i--) { cin>>c; if(c!='0') B += 1<<i; } if(A==B) cout<<0; else BFS(); }