八数码的A*与IDA*算法-搜索进阶练习1

八数码的A*与IDA*算法-搜索进阶练习1



题意:众所周知的八数码问题,就不再描述了。不得不说,为了练习A*以及IDA*就直接看题解了。 了解之后分析如下。

解题报告:

先谈A*。A*是一种 启发式搜索,也就是说在每次搜索前,先进行在状态空间中对每一个搜索的位置通过f(n) = g(n) + h(n)进行评估,得到最好的位置,再从这个位置,进行搜索直到目标。其中f(n)是从初始点经由节点n到目标点的估价函数,g(n) 是在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。

然后是IDA*。A*算法和迭代加深算法的结合。

1.搜索框架? A*(IDA*)算法。搜索直到终态结束,或该状态非法进行下一状态。
2.状态? 直接记录每个状态的maze、x位置信息以及A*所需f、g、h。同时使用hash值指向所有状态。
3.状态转移? x与上下左右互换,更新状态信息。
4.剪枝? 一个简单有效的剪枝逆序数为奇则必定无解。
5.hash以及h? hash可通过各位置逆序数乘位置散列值之和散列。h为所有数字块的曼哈顿距离和。

具体而言。A*通过构造优先队列实现搜索。构造优先队列时当f相同时按照g值从大到小排序,这样又是一个很给力的减枝。

#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;

//A*: 因为每次移动都会影响一个点的曼哈顿距离(不算x)
//构造h()为所有数字块的曼哈顿距离和,用逆序数hash(算x)
//根据逆序数奇偶性(不算x)减掉无法到达的情况,
struct Node
{
	char maze[3][3];
	int r, c, f, g, h;		//(x, y); f = g + h
							/*f(n) 是从初始点经由节点n到目标点的估价函数,
							g(n) 是在状态空间中从初始节点到n节点的实际代价,
							h(n) 是从n到目标节点最佳路径的估计代价。*/
	Node() {}
	Node(const Node & rhs)
	{
		for (int i = 0; i < 3; i++)
			for (int j = 0; j < 3; j++)
				maze[i][j] = rhs.maze[i][j];
		r = rhs.r, c = rhs.c, g = rhs.g, f = rhs.f, h = rhs.h;
	}
	//估价函数小的先出队,相同则实际代价大的先出队
	friend bool operator < (const Node & a, const Node & b)
	{
		if (a.f == b.f) return a.g < b.g;
		return a.f > b.f;
	}
}st;

const int dir[4][2] = { { 1,0 },{ 0,1 },{ -1,0 },{ 0,-1 } };//方向
char dr[5] = "drul";										//方向查找表
int pos[9][2] = {0,0,0,1,0,2,1,0,1,1,1,2,2,0,2,1,2,2};		//位置信息		
int fac[9] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320 };		//hash散列因子
char path[370000];
bool vis[370000];
int pre[370000];
int shash;

int h(const Node s)			//估价函数,偏移步数
{
	int ans = 0;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		{
			if (s.maze[i][j] == 'x') continue;
			int c = s.maze[i][j] - '1';
			int x = pos[c][0], y = pos[c][1];
			ans += (abs(x - i) + abs(y - j));
		}
	return ans;
}

int hash_node(const Node s) //hash散列,储存状态
{
	int ans = 0;
	char str[10];
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		{
			int cnt = 0;
			str[i * 3 + j] = s.maze[i][j];
			for (int k = 0; k < i * 3 + j; k++)
				if (str[k] > str[i * 3 + j]) cnt++;
			ans += fac[i * 3 + j] * cnt;
		}
	return ans;
}

bool check_h()				//逆序为奇返回true
{
	int cnt = 0;
	char str[10];
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		{
			str[i * 3 + j] = st.maze[i][j];
			if (st.maze[i][j] == 'x') continue;
			for (int k = 0; k < i * 3 + j; k++)
			{
				if (str[k] == 'x') continue;
				if (str[k] > str[i * 3 + j]) cnt++;
			}
		}
	return cnt & 1;
}

bool bfs()
{
	priority_queue<Node> q;
	q.push(st);
	memset(vis, false, sizeof(vis));
	vis[shash] = true;
	while (!q.empty())
	{
		Node u = q.top(); q.pop();
		int ihu = hash_node(u);
		for (int d = 0; d < 4; d++)
		{
			Node v(u);
			v.r = u.r + dir[d][0], v.c = u.c + dir[d][1];
			if (v.r < 0 || v.r >= 3 || v.c < 0 || v.c >= 3) continue;
			//更新地图
			v.maze[u.r][u.c] = v.maze[v.r][v.c];
			v.maze[v.r][v.c] = 'x';					
			v.g++, v.h = h(v); v.f = v.g + v.h;	//更新f = g + h
			int ihv = hash_node(v);
			if (vis[ihv]) continue;
			vis[ihv] = true; pre[ihv] = ihu;
			path[ihv] = dr[d];
			if (ihv == 0) return true;			//搜索结束
			q.push(v);
		}
	}
	return false;
}

void print(int h)
{
	if (h != shash)
	{
		print(pre[h]);
		putchar(path[h]);
	}
}

char in[100];

int main()
{
	while (gets_s(in))
	{
		for (int i = 0, x = 0, y = 0; in[i]; i++)
			if ((in[i] <= '8' && in[i] >= '1') || in[i] == 'x')
			{
				st.maze[x][y] = in[i];
				if (in[i] == 'x') st.r = x, st.c = y;
				y++;
				if (y == 3) x++, y = 0;
			}
		if (check_h()) { puts("unsolvable"); continue; }
		st.g = 0; st.h = h(st); st.f = st.h;
		if ((shash = hash_node(st)) == 0) { puts(""); continue; }
		int thash = bfs();
		print(0);			//递归输出答案
		puts("");
	}
	return 0;
}


而IDA*的话,和A*相同的h()函数。当前h深度下一层一层地进行搜索

#include <cstdio>
#include <algorithm>
using namespace std;

struct Node
{
	int r, c, f, g, h;
	char maze[3][3];
	Node(){}
	Node(const Node & rhs) {
		for (int i = 0; i < 3; i++)
			for (int j = 0; j < 3; j++)
				maze[i][j] = rhs.maze[i][j];
		r = rhs.r, c = rhs.c;
	}
}ts, s;

const int maxn = 370000;
const int dir[4][2] = { { 1,0 },{ 0,1 },{ -1,0 },{ 0,-1 } };
const char dr[5] = "drul";
const int fac[9] = { 1,1,2,6,24,120,720,5040,40320 };
const int pos[][2] = { 0,0,0,1,0,2,1,0,1,1,1,2,2,0,2,1,2,2 };
char path[maxn];
bool vis[maxn];
int deep;

int h(const Node st) {
	int ans = 0;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		{
			if (st.maze[i][j] == 'x') continue;
			int c = st.maze[i][j] - '1';
			ans += abs(pos[c][0] - i) + abs(pos[c][1] - j);
		}
	return ans;
}

int mhash(const Node st) {
	char str[10];
	int ans = 0;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		{
			str[i * 3 + j] = st.maze[i][j];
			int cnt = 0;
			for (int k = 0; k < 3 * i + j; k++)
				if (str[k] > str[i * 3 + j]) cnt++;
			ans += cnt * fac[i * 3 + j];
		}
	return ans;
}

bool check() {				//剪枝, 逆序数为奇返回1
	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 (s.maze[i][j] == 'x') continue;
			for (int k = 0; k < 3 * i + j; k++)
			{
				if (str[k] == 'x') continue;
				if (str[k] > str[i * 3 + j]) cnt++;
			}
		}
	return cnt & 1;
}

int dfs(int d) {
	//搜索完成
	if (h(ts) == 0) return true;
	//超过当前的搜索深度   
	if (h(ts) + d > deep) return false;
	//当前r、c
	int r = ts.r, c = ts.c;
	for (int dd = 0; dd < 4; dd++)
	{
		int nr = ts.r + dir[dd][0], nc = ts.c + dir[dd][1];
		if (nr < 0 || nc < 0 || nr > 2 || nc > 2) continue;
		ts.maze[r][c] = ts.maze[nr][nc];
		ts.maze[nr][nc] = 'x';
		int ihv = mhash(ts);
		if (vis[ihv]) {
			ts.maze[nr][nc] = ts.maze[r][c];
			ts.maze[r][c] = 'x';
			continue;
		}
		vis[ihv] = true;
		ts.r = nr, ts.c = nc;
		path[d] = dr[dd];
		if (dfs(d + 1)) return true;
		vis[ihv] = false;
		ts.r = r, ts.c = c;
		ts.maze[nr][nc] = ts.maze[r][c];
		ts.maze[r][c] = 'x';
	}
	return false;
}

int main() {
	char in[100];
	while (gets_s(in))
	{
		for (int i = 0, x = 0, y = 0; in[i]; i++)
			if (in[i] == 'x' || (in[i] > '0' && in[i] < '9'))
			{
				if (in[i] == 'x') s.c = y, s.r = x;
				s.maze[x][y++] = in[i];
				if (y == 3) y = 0, x++;
			}
		if (check()) { puts("unsolvable"); continue; }
		memset(vis, false, sizeof(vis));
		vis[mhash(s)] = true;
		ts = s; deep = 0;
		while (true)
		{
			if (dfs(0)) break;
			deep++;
		}
		for (int i = 0; i < deep; i++)
			putchar(path[i]);
		puts("");	
	}
	return 0;
}

最后打卡一下。之前Ubuntu的ibus默认的中文输入法双拼的,当时就感觉是一堆乱码,玩毛。不几天前,尝试用双拼了。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值