洛谷P1379 八数码难题(BFS)
洛谷P1379 八数码难题
八数码问题就是在 \(3\times 3\) 的棋盘上,摆有八个棋子,每个棋子上标有 1 至 8 的某一数字。棋盘中留有一个空格,我们用 0 来表示。给出一个初始局面,给出一个目标局面。请问从初始局面到目标局面的最小移动次数是多少。关于八数码问题的移动,洛谷中的Tips有提示。
思路:
最小操作次数,我们把移动看成一次状态的转移,那么这就是一个最短路问题。将题意抽象为:每次 0 可以与其周围的任一棋子交换位置,我们直接用BFS来写就可以了。状态可以用一维的字符串来表示,但是转移的时候是需要借助到这个棋盘的二维结构的,看代码领会一下。
代码:
体会一维和二维之间的转换。
#include <bits/stdc++.h>
using namespace std;
int d[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
string ans = "123804765"; //题面给的
int bfs(string s)
{
queue<string> q;
unordered_map<string, int> dis; //记录最少操作次数并判重,将二维的矩阵压到一维,可以降低时空复杂度。
q.push(s);
dis[s] = 0;
while (q.size())
{
string t = q.front();
q.pop();
if(t == ans) return dis[t];
int distance = dis[t];
int k = t.find('0');
int x = k / 3, y = k % 3; //把一维的字符串展开成二维的矩阵
for(int i = 0; i < 4; i++)
{
int xx = x + d[i][0], yy = y + d[i][1];
if(xx >= 0 && xx < 3 && yy >= 0 && yy < 3)
{
swap(t[k], t[xx * 3 + yy]);
if(!dis.count(t)) //若这个状态没有出现过
{
q.push(t);
dis[t] = distance + 1;
}
swap(t[k], t[xx * 3 + yy]); //回溯
}
}
}
return -1; //int函数中要防止未定义的行为。虽然题目明确说明了都可以到达目标状态,但是写代码要有鲁棒性
}
int main()
{
string s;
cin >> s;
cout << bfs(s);
}