AcWing 845. 八数码
题目描述
bfs算法求解
在一个 3×3 的网格中,1∼8 这 8 个数字和一个
x
恰好不重不漏地分布在这 3×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×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
分析
思路就是把每种状态看成一个节点,初始的状态距离为0,每切换一个状态,到下一个状态的距离就是当前状态距离+1,求到最后一个状态节点的距离
有三个难点
-
状态的表示:这里使用string来表示一个状态
1 2 3 x 4 6 7 5 8
用string表示该状态就是:
"123x46758"
最后的状态就是:
"12345678x"
-
距离的表示:这里使用
unordered_map<string, int> d
来表示到达某个状态的距离 -
状态转移
对于一个状态
"t = "123x46758"
,首先找到x
所在三维矩阵中的位置,然后在该位置上下左右四个方向进行交换得到新的状态,比如tt = "1234x6758"
然后看这个状态有没有出现过(使用d.count()
判断)没有出现过就将该状态的距离设置为t
状态的距离+1,即d[tt] = d[t] + 1
当转移过程中发现了end状态,就直接返回该状态的距离即可
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<unordered_map>
#include<queue>
#include<cstring>
using namespace std;
int bfs(string start)
{
string end = "12345678x"; // 结束状态
unordered_map<string, int> d; // 用来存到某个状态的距离
d[start] = 0;
queue<string> q;
q.push(start);
// 对每一个状态,进行bfs搜索
while(q.size())
{
string t = q.front();
q.pop();
int distance = d[t];
if(t == end) return distance; //找到了最终状态,bfs第一次发现的就是最短的路径
int k = t.find('x');
int ax = k / 3, ay = k % 3; // 得到x在start转成三维矩阵后的坐标
// 上下左右四个方向
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
for(int i = 0; i < 4; i++)
{
int nx = ax + dx[i], ny = ay + dy[i];
if(nx >= 0 && nx < 3 && ny >= 0 && ny < 3)
{
swap(t[k], t[nx * 3 + ny]);
if(!d.count(t)) //如果这个状态没有出现过
{
d[t] = distance + 1;
q.push(t);
}
swap(t[k], t[nx * 3 + ny]);
}
}
}
return -1;
}
int main()
{
string s, start;
for(int i = 0; i < 9; i++)
{
cin >> s;
start += s;
}
cout << bfs(start) << endl;
return 0;
}