BFS AcWing 845 八数码
BFS AcWing 845 八数码
题目描述
在一个 \(3×3\) 的网格中,\(1 \sim 8\) 这 \(8\) 个数字和一个 x
恰好不重不漏地分布在这 \(3 \times 3\) 的网格中。
例如:
1 2 3
x 4 6
7 5 8
在游戏过程中,可以把 x
与其上、下、左、右四个方向之一的数字交换(如果存在)。
我们的目的是通过交换,使得网格变为如下排列(称为正确排列):
1 2 3
4 5 6
7 8 x
例如,示例中图形就可以通过让 x
先后与右、下、右三个方向的数字交换成功得到正确排列。
交换过程如下:
1 2 3 1 2 3 1 2 3 1 2 3
x 4 6 4 x 6 4 5 6 4 5 6
7 5 8 7 5 8 7 x 8 7 8 x
现在,给你一个初始网格,请你求出得到正确排列至少需要进行多少次交换。
输入格式
输入占一行,将 \(3 \times 3\) 的初始网格描绘出来。
例如,如果初始网格如下所示:
1 2 3
x 4 6
7 5 8
则输入为:1 2 3 x 4 6 7 5 8
输出格式
输出占一行,包含一个整数,表示最少交换次数。
如果不存在解决方案,则输出 \(-1\)。
输入样例:
2 3 4 1 5 x 7 6 8
输出样例
19
思路
BFS算法的经典板子题。
注意以下几点:
- 输入的一维字符串要进行一维转二维的操作才能利用方向数组。
- 二维运算后必须再转一维字符串加入队列。
- 注意边界条件。
- unordered_map进行去重,如果重复就剪枝。
代码
#include <iostream>
#include <queue>
#include <unordered_map>
using namespace std;
int bfs(string start)
{
// 存储各次字符情况的状态图,string类型
queue<string> q;
// 存储变换到相应状态图所需的最小步数
unordered_map<string, int> d;
// 初始化队列
q.push(start);
d[start] = 0;// 初始步数为0
string END = "12345678x";
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, -1, 0, 1};
// 队列
while (q.size()){
// 获取当前状态
string t = q.front();
q.pop();
int dist = d[t];// 在哈希表里查找状态图t的最小步数
if (t == END) return dist;// 如果已经成为最后要求的状态,则返回步数
// 状态转移
int k = t.find('x');// 找到x的位置
int x = k / 3, y = k % 3;// 一维转二维
for (int i = 0; i < 4; i++){
int a = x + dx[i], b = y + dy[i];// 在3*3方格中上下左右遍历
if (a >= 0 && a < 3 && b >= 0 && b < 3){
swap(t[k], t[a * 3 + b]);// 注意这里要把二维转一维
// 如果x和数交换后出现了新的状态图
if (!d.count(t)){// 即在d中找键值t为空
d[t] = dist + 1;// t的最小步数更新为dist+1
q.push(t);// 把t入队,保证队列中所有状态图不会相同
}
swap(t[k], t[a * 3 + b]);// 注意,新的图t如果入队过了,就必须把刚交换的状态图调整回来
}
}
}
return -1;
}
int main()
{
string start;
for (int i = 0; i < 9; i++){
char c;
cin >> c;
start += c;
}
cout << bfs(start) << endl;
return 0;
}