A*算法 hdu1043 Eight 人工智能算法, 还有康拓展开得hash值

hdu1043这道八数码难题,有很多种境界解法。之前网上的博友互相抄袭的代码:离线搜索出所有状态的解,但是这种离线算法需要53MB左右的空间,显然MLE.杭电数据加强后就AC不了了。

后来在网上搜到了人工智能在线算法,强行正面搜索,解决了内存限制的问题。时间1s左右。

下面这个链接里面有八数码的八种境界。前两种境界我想到了。后面就……URL

A*算法,摘自网友。若看不懂,翻书

A* 算法的核心是公式 f = g + h。其中,g 起始状态到当前状态的距离,h 当前状态到目标状态的估计距离。 
BFS 是 A* 的一种特殊状况, h 恒等于 0。
h 的估计
如果h(n)< d(n)到目标状态的实际距离,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。 
如果h(n)=d(n),即距离估计h(n)等于最短距离,那么搜索将严格沿着最短路径进行, 此时的搜索效率是最高的。 
如果 h(n)>d(n),搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。

实现起来与 BFS 很相似,要用到优先队列。 
A* 算法的缺点是占用内存随问题的规模指数增长。据说 IDA* 能弥补这一点,以后再学。 

本题中把状态中每个数字到正确位置的欧氏距离和作为估计值。


//虽然路径不唯一,但使用A*算法得出结果和样例一样。而且这种人工智能在线算法,即快有省时
#include <iostream> 
#include <string>
#include <queue>
#include <cstring>
using namespace std;
struct State {
	int f, g, h; //A*算法
	int ary[9], pos;
	int hash;
	bool operator < (State tmp) const {
		return f == tmp.f ? g > tmp.g : f > tmp.f;
	} //重载小于运算符,用于优先队列
};
int fac[9] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320 };
int cantor(int ary[], int n) {
	int hash = 0;
	for (int i = 0; i < n; ++i) {
		int k = 0;
		for (int j = i + 1; j < n; ++j)
			if (ary[j] < ary[i]) ++k;
		hash += k * fac[n - 1 - i];
	}
	return hash; //不加1,hash值范围为 0:n! - 1
}
const int N = 362880, des = 0;//123456789的康拓展开值
const char op[5] = "udlr"; 
int dir[4][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
bool vis[N]; int pre[N]; char ch[N];
int get_h(int ary[]) { //状态中每个数字到正确位置的欧式距离和作为估计值
	int res = 0;
	for (int i = 0; i < 3; ++i) //行
		for (int j = 0; j < 3; ++j) //列
			res += abs((ary[i*3+j] - 1) / 3 - i) + abs((ary[i*3+j] - 1) % 3 - j);
	return res;
}
void print(int x) {
	if (pre[x] == -1) return;
	print(pre[x]);
	cout << ch[x];
}
void Astar(State st) {
	if (st.hash == des) { cout << endl; return; }
	memset(vis, 0, sizeof vis);
	priority_queue<State> pq;
	pq.push(st);
	vis[st.hash] = true;
	pre[st.hash] = -1;
	while (!pq.empty()) {
		st = pq.top(); pq.pop();
		int x = st.pos / 3, y = st.pos % 3;
		for (int i = 0; i < 4; ++i) {			
			int xx = x + dir[i][0], yy = y + dir[i][1];
			if (xx < 0 || xx > 2 || yy < 0 || yy > 2) continue;
			State tmp = st;
			tmp.pos = xx * 3 + yy;
			swap(tmp.ary[st.pos], tmp.ary[tmp.pos]);
			tmp.hash = cantor(tmp.ary, 9);
			if (vis[tmp.hash]) continue;
			pre[tmp.hash] = st.hash;
			ch[tmp.hash] = op[i];
			vis[tmp.hash] = true;
			if (tmp.hash == des) {
				print(0); cout << endl; return;
			}
			tmp.g++;
			tmp.h = get_h(tmp.ary);
			tmp.f = tmp.g + tmp.h;
			pq.push(tmp);
		}
	}
}
int main()
{
	ios::sync_with_stdio(false);
	char str[9];
	while (cin >> str[0]) {
		for (int i = 1; i < 9; ++i)
			cin >> str[i];
		State s;
		for (int i = 0; i < 9; ++i) {
			if (str[i] == 'x') {
				s.pos = i;
				s.ary[i] = 9;
			}
			else s.ary[i] = str[i] - '0';
		}
		s.g = 0; s.h = get_h(s.ary);
		s.f = s.g + s.h;
		s.hash = cantor(s.ary, 9);
		int k = 0; //通过逆序数的奇偶判断是否可解
		for (int i = 0; i < 9; ++i)
			for (int j = 0; j < i; ++j) //求第i个数前有几个大于ary[i]的数
				if (s.ary[j] != 9 && s.ary[i] < s.ary[j])
					++k; //勉强可以理解,移动一下,逆序数改变偶数值
		if(k & 1) cout << "unsolvable" << endl;
		else Astar(s);
	}

	return 0;
}

poj1077 在北大OJ,内存给了64MB,用5MB就够了

#include <iostream> 
#include <string>
#include <queue>
#include <cstring>
using namespace std;
struct State {
	int ary[9], pos;
	int hash;
	string path;
};
string path;
int fac[9] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320 };
int cantor(int ary[], int n) {
	int hash = 0;
	for (int i = 0; i < n; ++i) {
		int k = 0;
		for (int j = i + 1; j < n; ++j)
			if (ary[j] < ary[i]) ++k;
		hash += k * fac[n - 1 - i];
	}
	return hash + 1;
}
const int N = 362880 + 1, des = 46234;//123456780的康拓展开值
const string s = "udlr";
int dir[4][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };//udlr
bool vis[N];
bool bfs(State st) {
	if (st.hash == des) return true;
	memset(vis, 0, sizeof vis);
	queue<State> q;
	q.push(st);
	vis[st.hash] = true;
	while (!q.empty()) {
		st = q.front(); q.pop();
		int x = st.pos / 3, y = st.pos % 3;
		for (int i = 0; i < 4; ++i) {			
			int xx = x + dir[i][0], yy = y + dir[i][1];
			if (xx < 0 || xx > 2 || yy < 0 || yy > 2) continue;
			State tmp = st;
			tmp.pos = xx * 3 + yy;
			tmp.ary[st.pos] = tmp.ary[tmp.pos];
			tmp.ary[tmp.pos] = 0; 
			tmp.hash = cantor(tmp.ary, 9);
			if (vis[tmp.hash]) continue;
			vis[tmp.hash] = true;
			tmp.path += s[i];
			if (tmp.hash == des) { path = tmp.path; return true;}
			q.push(tmp);
		}
	}
	return false;
}
int main()
{
	ios::sync_with_stdio(false);
	string str = string(9, 0);
	while (cin >> str[0]) {
		for (int i = 1; i < 9; ++i)
			cin >> str[i];
		State start;
		for (int i = 0; i < 9; ++i) {
			if (str[i] == 'x') {
				start.ary[i] = 0;
				start.pos = i;
			}
			else start.ary[i] = str[i] - '0';
		}
		start.hash = cantor(start.ary, 9);
		path = "";
		if (bfs(start)) cout << path << endl;
		else cout << "unsolvable" << endl;
	}

	return 0;
}

PS今天见到一个奇怪的操作,(自带代码混淆语言)

printf("%d%c", ary[i], " \n"[i==n]);输出数据见空格,最后一个换行,双引号返回一个指针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值