1077 Eight:八数码问题变形:从bfs到双向bfs+hash到A*启发式搜索的进化史

题目大意

添加链接描述
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用x来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为12345678x),找到一种移动方法,实现从初始布局到目标布局的转变。如果不行输出unsolved。

思路分析

八数码问题的变化版,八数码的三种解法,无非是多了存储路径,以及局面不可解的问题。我们选择使用双向dfs,
在这里插入图片描述
效率还是可以的,这里我采用一个优化,将四种搜索策略对称分布

//右上下左四种策略
ll dx[4] = { 0,-1,1,0 }, dy[4] = { 1,0,0,-1 };
map<ll, char> sta; //将策略映射到步骤 
sta[0] = 'r', sta[1] = 'u', sta[2] = 'd', sta[3] = 'l';

对正向搜索而言,我们不断的把搜索策略对应的步骤加到上一个状态对应的步骤即可,对反向搜索而言,我们选取第i个策略进行搜索,就相当于正向采用第3-i个策略进行搜索,我们将这个策略加到反向搜索步骤的开始(具体见代码注释),当正向和反向搜索碰面的时候,我们直接先输出正向搜索的步骤,再输出反向搜索的步骤即可。

#include<iostream>
#include<string>
#include<map>
#include<queue>
#include<string.h>
using namespace std;

#define ll long long

//右上下左四种策略
ll dx[4] = { 0,-1,1,0 }, dy[4] = { 1,0,0,-1 };

int main() {
	map<ll, ll> m;//0:未搜索  1:正向搜索过  2:反向搜索过
	map<ll, string> step;
	map<ll, char> sta; //将策略映射到步骤 
	sta[0] = 'r', sta[1] = 'u', sta[2] = 'd', sta[3] = 'l';
	queue<ll> q;
	ll ss = 0, tt = 123456780,  x, y;
	char tmp;
	for (int i = 0; i < 3; i++) {
		for (int j = 0; j < 3; j++) {
			cin >> tmp;
			if (tmp <= '9' && tmp >= '0')ss = ss * 10 + tmp - '0';
			else {
				ss *= 10;
			}
		}
	}
	q.push(ss); q.push(tt); m[ss] = 1, m[tt] = 2;

	while (!q.empty()) {
		ll now = q.front(); q.pop(); ll c[3][3], n = now;

		for (int i = 2; i >= 0; i--) 
			for (int j = 2; j >= 0; j--) {
				c[i][j] = n % 10; n /= 10;
				if (c[i][j] == 0) { x = i, y = j; }
			}
		
		for (int i = 0; i < 4; i++) {
			ll xx = x + dx[i], yy = y + dy[i];
			if (xx < 0 || xx>2 || yy < 0 || yy>2)continue;
			swap(c[xx][yy], c[x][y]);
			ll next = 0;
			for (int i = 0; i < 3; i++)for (int j = 0; j < 3; j++)next = next * 10 + c[i][j];
			if (m[next] == m[now]) {//衍生出的状态已经被同一方向搜索过
				swap(c[xx][yy], c[x][y]); continue;
			}
			if (m[next] + m[now] == 3) {//正反搜索碰面,结果出世,正向搜索在前输出步骤
				if (m[now] == 1)cout << step[now] + sta[i] + step[next] << endl;
				else cout << step[next] + sta[3 - i] + step[now] << endl;
				return 0;
			}
			m[next] = m[now];//搜索方向保持一致
			if (m[next] == 1) { step[next] = step[now] + sta[i]; }//正向的话直接将策略放到最后
			else { step[next] = sta[3 - i] + step[now]; }//反向向上相当于正向向下,取反并加到最前
			q.push(next);
			swap(c[xx][yy], c[x][y]);//别忘了swap
		}
	}
	//一直到最后都没结果
	cout << "unsolvable" << endl;
}
发布了211 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/csyifanZhang/article/details/105308083