八数码难题(双向搜索)

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;
}

posted @ 2025-08-29 21:54  SigmaToT  阅读(15)  评论(0)    收藏  举报