計蒜客 ·八數碼問題
【BFS】
初見安~本題選自計蒜客。
Description
八方塊移動遊戲要求從一個含 8 個數字(用 1-8 表示)的方塊以及一個空格方塊(用 0 表示)的 3 × 3 矩陣的起始狀態開始,不斷移動該空格方塊以使其和相鄰的方塊互換,直至達到所定義的目標狀態。空格方塊在中間位置時有上、下、左、右 4 個方向可移動,在四個角落上有 2個方向可移動,在其他位置上有 3 個方向可移動。例如,假設一個 3× 3 矩陣的初始狀態為:
8 0 3
2 1 4
7 6 5
目標狀態為:
1 2 3
8 0 4
7 6 5
則一個合法的移動路徑為:
8 0 3 > 8 1 3 > 8 1 3 > 0 1 3 > 1 0 3 > 1 2 3
2 1 4 > 2 0 4 > 0 2 4 > 8 2 4 > 8 2 4 > 8 0 4
7 6 5 > 7 6 5 > 7 6 5 > 7 6 5 > 7 6 5 > 7 6 5
另外,在所有可能的從初始狀態到目標狀態的移動路徑中,步數最少的路徑被稱為最短路徑;在上面的例子中,最短路徑為 5 。如果不存在從初試狀態到目標狀態的任何路徑,則稱該組狀態無解。 請設計有效的(細節請見評分規則)演算法找到從八方塊的某初試狀態到某目標狀態的所有可能路徑中的最短路徑。
Input
程式需讀入初始狀態和目標狀態,這兩個狀態都由 9 個數字組成( 0 表示空格, 1-8 表示 8個數字方塊),每行 3 個數字,數字之間用空格隔開。
Output
如果輸入資料有解,輸出一個表示最短路徑的非負的整數;如果輸入資料無解,輸出 -1 。
Sample Input
8 0 3
2 1 4
7 6 5
1 2 3
8 0 4
7 6 5
Sample Output
5
題解
本題就不得不用BFS來做了——過程為交換‘0’和上下左右任意方向的數字,直到拼出目標狀態。就挺有BFS“不撞南牆不回頭”的理念。大致思路是很簡單的。
在儲存和設定變數上,為了對比目標狀態,我們可以為走的每一步後的狀態——二維陣列,開一個結構體並重定向“==”。但依據BFS的搜尋套路,需要一個vis來記錄是否走過這一步——即這個狀態是否湊出來過。我們可以選擇在結構體裡順便標一個號,or用一個set集合去重——但set會有一個內部的自動排序,所以我們還要重定向一個“<”or">"(因為我們實際上用不到)
程式碼如下——
#include<bits/stdc++.h> using namespace std; int sa,sb; int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; bool in(int a,int b)//檢查是否越界 { if(a>=0&&a<=2&&b>=0&&b<=2) return true; return false; } struct node { int a[3][3];//狀態 int d,x,y;//步數及‘0’的位置 bool operator < (const node &x) const { for(int i=0;i<3;i++) for(int j=0;j<3;j++) { if(a[i][j]!=x.a[i][j]) return a[i][j]<x.a[i][j]; } return false; } bool operator == (const node &x) const { for(int i=0;i<3;i++) for(int j=0;j<3;j++) { if(a[i][j]!=x.a[i][j]) return false; } return true; } }from,to; int bfs() { queue<node> q; q.push(from); set<node> st; st.insert(from);//存入原狀態 while(!q.empty()) { node now=q.front(); q.pop(); for(int i=0;i<4;i++) { int ta=now.x+dir[i][0]; int tb=now.y+dir[i][1]; if(in(ta,tb)) { node tmp=now; tmp.d++; swap(tmp.a[ta][tb],tmp.a[now.x][now.y]); if(!st.count(tmp)) { if(tmp==to) return tmp.d;//達到目標狀態,直接返回 tmp.x=ta;tmp.y=tb;//沒有則存入繼續 q.push(tmp); st.insert(tmp); } } } } return -1; } int main() { for(int i=0;i<3;i++) for(int j=0;j<3;j++) { cin>>from.a[i][j]; if(from.a[i][j]==0)//找到了初始的‘0’ { from.x=i;from.y=j; } } for(int i=0;i<3;i++) for(int j=0;j<3;j++) cin>>to.a[i][j]; cout<<bfs()<<endl; return 0; }
迎評:)
——End——