八数码难题(双向搜索)
P1379 八数码难题
题目描述
在 \(3\times 3\) 的棋盘上,摆有八个棋子,每个棋子上标有 \(1\) 至 \(8\) 的某一数字。棋盘中留有一个空格,空格用 \(0\) 来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为 \(123804765\)),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
输入格式
输入初始状态,一行九个数字,空格用 \(0\) 表示。
输出格式
只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数。保证测试数据中无特殊无法到达目标状态数据。
输入输出样例
输入 #1
283104765
输出 #1
4
说明/提示
样例解释

图中标有 \(0\) 的是空格。绿色格子是空格所在位置,橙色格子是下一步可以移动到空格的位置。如图所示,用四步可以达到目标状态。
并且可以证明,不存在更优的策略。
思路+代码
这道题求的是最少移动次数,而且相当于是在网格上,所以我们可以使用bfs,因为bfs具有最优性。
这样的话,状态一共有 \(9!\) 种,这样应该是能过的。
但是如果我们想要追求更卓越的复杂度,就可以使用双向搜索,即同时从起点状态和重点状态开始bfs,这样bfs的深度就会减半,因此如果本来复杂度是 \(a^k\) 的,现在就会变成 \(\sqrt{a^k}\),这样时间就会大大缩减。
点击查看代码
#include <iostream>
#include <algorithm>
#include <map>
#include <queue>
using std::cin;
using std::cout;
const int N = 100;
int begin, end;
int a[N];
std::map<int, int> m[2]; // 存每种状态的最小步数,直接用数组会炸空间
std::queue<std::pair<int, bool> > q;
int change(int x, int opt)
{
for (int i = 9; i >= 1; --i) // 状态“解压缩”
a[i] = x % 10, x /= 10;
int p;
for (int i = 1; i <= 9; ++i)
{
if (a[i] == 0)
{
p = i;
break;
}
} // 寻找 0 的那个点
if (opt == 1) // 往上
{
if (p <= 3) // 已经在最上面了
return -1;
std::swap(a[p], a[p - 3]);
}
if (opt == 2) // 往下
{
if (p >= 7) // 已经在最下面了
return -1;
std::swap(a[p], a[p + 3]);
}
if (opt == 3) // 往左
{
if (p % 3 == 1) // 已经在最左面了
return -1;
std::swap(a[p], a[p - 1]);
}
if (opt == 4) // 往右
{
if (p % 3 == 0) // 已经在最右面了
return -1;
std::swap(a[p], a[p + 1]);
}
int ret = 0;
for (int i = 1; i <= 9; ++i)
ret = ret * 10 + a[i];
return ret;
}
void check(int x, std::pair<int, bool> y)
{
if (x == -1)
return;
if (m[y.second ^ 1].count(x))
{
cout << m[y.second ^ 1][x] + m[y.second][y.first] + 1 << '\n';
exit(0);
}
else
{
m[y.second][x] = m[y.second][y.first] + 1;
q.push({x, y.second});
}
}
int main()
{
cin >> begin;
end = 123804765;
if (begin == end)
{
cout << 0 << '\n';
return 0;
}
q.push({begin, 0});
q.push({end, 1});
while (!q.empty())
{
std::pair<int, bool> now = q.front();
q.pop();
int x = change(now.first, 1);
check(x, now);
x = change(now.first, 2);
check(x, now);
x = change(now.first, 3);
check(x, now);
x = change(now.first, 4);
check(x, now);
}
return 0;
}

浙公网安备 33010602011771号