杭电OJ 1043(C++)

本文介绍了一种使用A*算法结合康托展开进行迷宫求解的方法。通过定义迷宫状态结构体,实现优先队列排序规则,并利用康托展开求迷宫状态的哈希值,确保了状态的唯一性和快速访问。同时,通过计算曼哈顿距离作为启发式函数,提高了A*算法的效率。最后,通过广度优先搜索实现了迷宫的求解。
#include <iostream>
#include <cstring>
#include <queue>
#include <cmath>
#include <cctype>
using namespace std;

struct S //迷宫状态
{
	char maze[3][3]; //迷宫数组
	int x, y; //X的坐标 
	int g, h, f; //A*算法的g、h、f
	S(){}
	S(const S & ts) //复制构造函数
	{
		memcpy(maze, ts.maze, sizeof(maze));
		x = ts.x; y = ts.y;
		g = ts.g; h = ts.h; f = ts.f;
	}
	friend bool operator <(const S& a, const S& b) //优先队列的排序规则
	{
		if (a.f == b.f)
			return a.g < a.g;
		return a.f > b.f;
	}
}s;

const int fac[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320 }; //康托展开所需的阶乘
bool vis[363000]; //迷宫状态的访问状态
int pre[363000]; //迷宫状态的父状态
char op[363000]; //X移动方向

//康托展开求哈希值
int inv_hash(S ts)
{
	char str[10];
	int ans = 0; //康托展开值
	for (int i = 0; i < 3; i++)
	for (int j = 0; j < 3; j++)
	{
		str[i * 3 + j] = ts.maze[i][j];
		int cnt = 0;
		for (int k = i * 3 + j - 1; k >= 0; k--)
		if (str[k] > str[i * 3 + j]) cnt++;
		ans += fac[i * 3 + j] * cnt;
	}
	return ans;
}

const int pos[][2] = { { 0, 0 }, { 0, 1 }, { 0, 2 }, { 1, 0 }, { 1, 1 }, { 1, 2 }, { 2, 0 }, { 2, 1 }, { 2, 2 } };
//A*算法中的h,此处取所有数字与其正确位置的曼哈顿距离之和
int h(S ts)
{
	int val = 0;
	for (int i = 0; i < 3; i++)
	for (int j = 0; j < 3; j++)
	{
		if (ts.maze[i][j] == 'x')
			continue;
		int c = ts.maze[i][j] - '1';
		val += abs(pos[c][0] - i) + abs(pos[c][1] - j);
	}
	return val;
}

const int dir[4][2] = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } }; //移动方向
bool bfs() //广度优先搜索
{
	memset(vis, 0, sizeof(vis));
	priority_queue<S> qu; //优先队列
	qu.push(s);
	while (!qu.empty())
	{
		S u = qu.top();   qu.pop();
		int ihu = inv_hash(u);

		for (int i = 0; i < 4; i++)
		{
			S v = u;
			v.x += dir[i][0];
			v.y += dir[i][1];
			if (v.x < 0 || v.x >= 3 || v.y < 0 || v.y >= 3) continue;
			//移动X
			v.maze[u.x][u.y] = u.maze[v.x][v.y];
			v.maze[v.x][v.y] = 'x';
			
			v.g += 1;  v.h = h(v); v.f = v.g + v.h; //重新计算g,h,f值
			int ihv = inv_hash(v); //重新计算哈希值

			if (vis[ihv]) //当前迷宫状态已访问
				continue;
			vis[ihv] = 1;
			
			pre[ihv] = ihu; //记录父节点
			if (i == 0) op[ihv] = 'd';
			else if (i == 1) op[ihv] = 'r';
			else if (i == 2) op[ihv] = 'u';
			else if (i == 3) op[ihv] = 'l';
			if (ihv == 0)
				return true;
			qu.push(v);
		}
	}
	return false;
}

inline bool inv_check() //如果逆序数为奇数,则无解 
{
	char str[10];
	int cnt = 0;
	for (int i = 0; i < 3; i++)
	for (int j = 0; j < 3; j++)
	{
		str[i * 3 + j] = s.maze[i][j];
		if (str[i * 3 + j] == 'x')continue;
		for (int k = i * 3 + j - 1; k >= 0; k--)
		{
			if (str[k] == 'x') continue;
			if (str[k]>str[i * 3 + j])
				cnt++;
		}
	}
	return !(cnt & 1);
}

char in[30]; //输入
char stk[100]; //输出
int main()
{
	while (cin.getline(in, 30))
	{
		for (int i = 0, x = 0, y = 0; in[i]; i++)
		{
			if (isdigit(in[i]) || in[i] == 'x')
			{
				s.maze[x][y] = in[i];
				if (in[i] == 'x') { s.x = x;  s.y = y; }
				y++;
				if (y == 3)
				{
					y = 0;
					x++;
				}
					
			}
		}
		if (!inv_check())
		{
			cout << "unsolvable" << endl;
			continue;
		}
		s.g = 0;  s.h = h(s);  s.f = s.h;

		//如果输入数据就是目标状态
		int shash = inv_hash(s);
		if (shash == 0)
		{
			cout << endl;
			continue;
		}

		bfs();

		int top = -1, thash = 0;
		while (thash != shash)
		{
			stk[++top] = op[thash];
			thash = pre[thash];
		}
		for (int i = top; i >= 0; i--)
			cout << stk[i];
		cout << endl;
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值