八数码 A star启发式搜索

原题链接
在一个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
把“X”与上下左右方向数字交换的行动记录为“u”、“d”、“l”、“r”

现在,给你一个初始网格,请你通过最少的移动次数,得到正确排列。

输入格式
输入占一行,将3×3的初始网格描绘出来。

例如,如果初始网格如下所示:
1 2 3
x 4 6
7 5 8

则输入为:1 2 3 x 4 6 7 5 8

输出格式
输出占一行,包含一个字符串,表示得到正确排列的完整行动记录。如果答案不唯一,输出任意一种合法方案即可。

如果不存在解决方案,则输出”unsolvable”

输入样例:
2 3 4 1 5 x 7 6 8
输出样例
ullddrurdllurdruldr

A star算法是一直沿着某条估计出来的路劲搜索的,且估计出来的路劲一定要小于等于实际路劲
A star的形式与Dijkstra的形式非常接近(Dijkstra估计函数为0),但是Dijkstra在每次出队之后就已经定下来出队点已是最小距离,而A star不行。A star只能保证终点状态是最小的。
八数码问题若序列中的逆序对数量为奇数个则无法走到终点
这里估价函数采用曼哈顿距离,为理想状态每个位置(如‘1’ 在(1,1)上)到终点位置('1'的终点坐标(0,0))的曼哈顿距离(则此时'1'的曼哈顿距离为2)之和。这样的算出来的距离一定小于等于实际上该状态到终点的最小距离

#include
#include
#include
#include
#include
using namespace std;

int dx[] = {
     -1,0,1,0};
int dy[] = {
     0,1,0,-1};
char op[5] = "urdl";

int f(string state)
{
     //曼哈顿估价函数,估价值为每个位置离终点的曼哈顿距离之和
	int res = 0;
	for(int i = 0; i < 9; i++)
		if(state[i] != 'x')
		{
     
			int t = state[i] - '1';
			res += abs(t/3 - i/3) + abs(t%3 - i%3);
		}
		
	return res;
}

void bfs(string start)
{
     
	string end = "12345678x";
	
	unordered_map<string,int> dist;	//存真实距离
	unordered_map<string,pair<char,string>>pre; //存路径
	priority_queue<pair<int,string>,vector<pair<int,string> >,greater<pair<int,string> > > heap;//存离终点的估计距离
	dist[start] = 0;
	heap.push({
     dist[start] + f(start),start});
	
	while(heap.size())
	{
     
		auto t = heap.top();
		heap.pop();
		
		string state = t.second , source = t.second;
		int distance = t.first;
		
		if(state == end)
		{
      //到达终点,输出路径
			string path;
			while(start != end)
			{
     
				path += pre[end].first;
				end = pre[end].second;
			}
			reverse(path.begin(),path.end());
			cout << path << endl;
			return;
		}
		
		int location;
		for(int i = 0; i < 9; i++)	
			if(state[i] == 'x')	location = i;//找到'x'的位置
		
		for(int i = 0; i < 4; i++)
		{
     //向四个方向扩展
			state = source;
			int x = (location / 3) + dx[i];
			int y = (location % 3) + dy[i];
			
			swap(state[location],state[x*3 + y]);
			
			if(x < 0 || x >= 3 || y < 0 || y >= 3)	continue;
			if(dist.count(state) == 0 || dist[state] > dist[source] + 1)
			{
      // 如果是第一次扩展到这个状态 或 走到这个状态有更小的步数
				dist[state] = dist[source] + 1;
				heap.push({
     dist[state] + f(state),state});
				pre[state] = {
     op[i],source};
			}			
		}
	}
	
}

int main()
{
     
	string start;
	
	for(int i = 0; i < 9;i++)
	{
     
		char c;
		cin >> c;
		start += c;
	}
	
	int cnt = 0;
	for(int i = 0; i < 9; i++)
		for(int j = i; j < 9; j++)
			if(start[i] != 'x' && start[j] != 'x' && start[i] > start[j])
				cnt++;
	
	if(cnt & 1)	puts("unsolvable");
	else bfs(start);
	
	return 0;
} 

你可能感兴趣的:(八数码 A star启发式搜索)