1426: [藍橋杯][歷屆試題] 九宮重排(雙端bfs)
阿新 • • 發佈:2021-02-06
題目
題目連結
如下面第一個圖的九宮格中,放著 1~8 的數字卡片,還有一個格子空著。與空格子相鄰的格子中的卡片可以移動到空格中。經過若干次移動,可以形成第二個圖所示的局面。
我們把第一個圖的局面記為:12345678.
把第二個圖的局面記為:123.46758
顯然是按從上到下,從左到右的順序記錄數字,空格記為句點。
本題目的任務是已知九宮的初態和終態,求最少經過多少步的移動可以到達。如果無論多少步都無法到達,則輸出-1。
輸入
輸入第一行包含九宮的初態,第二行包含九宮的終態。
輸出
輸出最少的步數,如果不存在方案,則輸出-1。
樣例輸入
12345678.
樣例輸出
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;
}