1. 程式人生 > >BFS —— 資訊學一本通(1451:棋盤遊戲)

BFS —— 資訊學一本通(1451:棋盤遊戲)

題目描述

在一個4*4的棋盤上有8個黑棋和8個白棋,當且僅當兩個格子有公共邊,這兩個格子上的棋是相鄰的。移動棋子的規則是交換相鄰兩個棋子。現在給出一個初始棋盤和一個最終棋盤,要求你找出一個最短的移動序列使初始棋盤變為最終棋盤。
Klux說:“這麼簡單的題目,我都會做!”

輸入格式:

第1到4行每行四個數字(1或者0),描述了初始棋盤
接著是一個空行
第6到9行每行四個數字,描述了最終棋盤

輸出格式:

輸出只有一行是一個整數n,表示最少的移動步數。

輸入樣例#1:
1111
0000
1110
0010

1010
0101
1010
0101

輸出樣例#1:

4

解題思路

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();
}