1. 程式人生 > 其它 >1426: [藍橋杯][歷屆試題] 九宮重排(雙端bfs)

1426: [藍橋杯][歷屆試題] 九宮重排(雙端bfs)

技術標籤:c++筆記bfs

題目

題目連結
如下面第一個圖的九宮格中,放著 1~8 的數字卡片,還有一個格子空著。與空格子相鄰的格子中的卡片可以移動到空格中。經過若干次移動,可以形成第二個圖所示的局面。

圖片
我們把第一個圖的局面記為:12345678.
把第二個圖的局面記為:123.46758
顯然是按從上到下,從左到右的順序記錄數字,空格記為句點。
本題目的任務是已知九宮的初態和終態,求最少經過多少步的移動可以到達。如果無論多少步都無法到達,則輸出-1。
輸入
輸入第一行包含九宮的初態,第二行包含九宮的終態。
輸出
輸出最少的步數,如果不存在方案,則輸出-1。
樣例輸入
12345678.

123.46758
樣例輸出
3

思路:
本題直接在棋盤上動手不是很好,要將每個棋盤的狀態用矩陣壓縮變成字串進行bfs比較好,使用map方便進行判斷是否走到過這種局勢,queue更是bfs不能缺少的。

#include <iostream>
#include <string>
#include <map>
#include <queue>
#include <algorithm>
using namespace std;
string first, last, str1, str2;        //這裡選擇string類作為輸出
int
flag, x, y; map<string, int>dis, vis; //用map去重同時方便查詢,dis表示開始找的點到當前點的距離 queue<string>q1, q2; //兩個佇列 char m[3][3]; //九宮字元陣列 const int dir[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; bool in(int x, int y){ //判斷是否超出九宮 return x >= 0 && x < 3 &&
y >= 0 && y < 3; } void toMatrix(string str){ //把字串變成九宮字元陣列 for(int i = 0; i < str.size(); i++){ //演算法實現比較簡單,模擬一下即可 m[i / 3][i % 3] = str[i]; } } string toString(){ //把九宮字元陣列轉化為字串 string str; for(int i = 0; i < 3; i++){ for(int j = 0; j < 3; j++){ str.push_back(m[i][j]); //一個一個push_back } } return str; } int dbfs(){ //重頭戲來了 q1.push(first); //q1從起點開始找 dis[first] = 1; //一開始賦值1 vis[first] = 1; //q1訪問過的賦值為1 q2.push(last); //q2從終點開始找 dis[last] = 1; vis[last] = 2; //q2訪問過的賦值為2 while(!q1.empty() && !q2.empty()){//如果其中一個佇列為空,還沒有找到解,說明無解 if(q1.size() < q2.size()){ //根據佇列的容量來判斷是從哪一端找,誰少誰出列 str1 = q1.front(); q1.pop(); flag = 1; //作一下是哪一個隊列出列的標記 } else{ str1 = q2.front(); q2.pop(); flag = 2; } toMatrix(str1); //轉化為九宮陣列 for(int i = 0; i < 3; i++){ //找小數點 for(int j = 0; j < 3; j++){ if(m[i][j] == '.'){ x = i; y = j; } } } for(int i = 0; i < 4; i++){ //四個方向開始試探 int tx = x + dir[i][0]; int ty = y + dir[i][1]; if(in(tx, ty)){ swap(m[x][y], m[tx][ty]); //試探 str2 = toString(); if(!dis.count(str2)){ //如果沒有訪問,就一下標記 dis[str2] = dis[str1] + 1; vis[str2] = vis[str1]; //這樣寫可以避免判斷是哪一端找的str2 if(flag == 1){ //誰出的列誰就入隊 q1.push(str2); }else if(flag == 2){ q2.push(str2); } } else{ //如果已經訪問判斷標記是否重合 if(vis[str1] + vis[str2] == 3){ int ans = dis[str1] + dis[str2] - 1;//這裡記得減一 return ans; } } swap(m[x][y], m[tx][ty]); //試探完畢一定要記得要回溯 } } } return -1; //沒有找到,返回-1 } int main(){ cin >> first >> last; if(first == last){ cout << 0 << endl; } else{ cout << dbfs() << endl; } return 0; }