LA 6665 —— Dragon’s Cruller(搜索,A*)

题目:Dragon’s Cruller

从头到尾都透露出八数码的气息的一个题。。。果断写A*了。

没做过八数码问题或A*搜索的强烈建议先做这道题:Eight

相关资料跟题解网上一大堆。

这道题跟八数码的不同在于,可以从一个边界跑到另一个边界,按照图中的字母对应。并且这题给出了横向移动和纵向移动各自的消耗,并且确保一个状态能到达另一个状态,求始态到终态的最低消耗。

我是利用弗罗伊德算法预处理出每个位置到其他位置的最低消耗,用于估价函数的计算。

当从队列里面取出的元素是目标状态时停止搜索。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int vis[1000000];
char str[10], ss[10];
int fac[10], target[10];
bool has[10];
int dis[10][10];
int hg[10][2], vg[10][2];//从某个位置出发可以到达的位置
int hi[10][2], vi[10][2];//可以到达该位置的位置
int find(char s[]){//康托展开
	int res = 0;
	memset(has, 0, sizeof(has));
	for(int i=0; i<9; i++){
		int x = s[i]-'0', y=0;
		for(int j=1; j<x; j++){
			if(!has[j])	y++;
		}
		res += y*fac[8-i];
		has[x] = 1;
	}
	return res;
}
int get_h(const char s[]){//估价函数
	int res = 0;
	for(int i=0; i<9; i++){
		res += dis[i][target[s[i]-'0']];
	}
	return res;
}
struct Node{
	char s[10];
	int g;
	Node(){}
	Node(char s[], int g):g(g){
		strcpy(this->s, s);
	}
	bool operator < (const Node& o)const{
		return g+get_h(s) > o.g+get_h(o.s);
	}
};
void init(){
	fac[0] = 1;
	for(int i=1; i<9; i++)	fac[i] = fac[i-1]*i;
	for(int i=0; i<9; i++){//求出i位置出发可以到达的位置,用于计算移动消耗
		hg[i][0] = (i+1)%9;
		hg[i][1] = (i+8)%9;
		vg[i][0] = (i+3)%9;
		vg[i][1] = (i+6)%9;
	}
	memset(vi, -1, sizeof(vi));
	memset(hi, -1, sizeof(hi));
	for(int i=0; i<9; i++){
		for(int j=0; j<2; j++){
			int x = vg[i][j];//反过来记录可以到达x的位置,用于搜索中移动的计算
			if(vi[x][0]==-1)	vi[x][0] = i;
			else	vi[x][1] = i;
			x = hg[i][j];
			if(hi[x][0]==-1)	hi[x][0] = i;
			else	hi[x][1] = i;
		}
	}
}
int A, B;
void cal(){
	for(int i=0; i<9; i++){
		for(int j=0; j<9; j++)	dis[i][j] = 1000000000;
		for(int j=0; j<2; j++){
			dis[i][vg[i][j]] = B;
			dis[i][hg[i][j]] = A;
		}
		dis[i][i] = 0;
	}
	//弗罗伊德算法
	for(int k=0; k<9; k++){
		for(int i=0; i<9; i++){
			for(int j=0; j<9; j++)
				dis[i][j] = min(dis[i][j], dis[i][k]+dis[k][j]);
		}
	}
}
int main(){
	init();
	int T, d, x;
	while(~scanf("%d %d", &A, &B) && (A||B)){
		for(int i=0; i<9; i++){
			scanf("%d", &d);
			if(d==0)	d = 9;
			str[i] = d+'0';
		}
		str[9] = '\0';
		for(int i=0; i<9; i++){
			scanf("%d", &d);
			if(d==0)	d = 9;
			target[d] = i;
			ss[i] = d+'0';
		}
		T = find(ss);
		if(T==find(str)){
			puts("0");
			continue;
		}
		cal();
		memset(vis, -1, sizeof(vis));
		priority_queue<Node> Q;
		Q.push(Node(str, 0));
		while(!Q.empty()){
			Node nd = Q.top(); Q.pop();
			x = find(nd.s);
			if(x==T){
				printf("%d\n", nd.g);
				break;
			}
			if(~vis[x] && nd.g >= vis[x])	continue;
			vis[x] = nd.g;
			x = 0;
			while(nd.s[x]!='9')	x++;
			for(int i=0; i<2; i++){
				strcpy(ss, nd.s);
				ss[x] = ss[vi[x][i]];
				ss[vi[x][i]] = '9';
				d = find(ss);
				if(vis[d]==-1 || nd.g+B<vis[d]){
					Q.push(Node(ss, nd.g+B));
				}
				ss[vi[x][i]] = ss[x];
				ss[x] = ss[hi[x][i]];
				ss[hi[x][i]] = '9';
				d = find(ss);
				if(vis[d]==-1 || nd.g+A<vis[d]){
					Q.push(Node(ss, nd.g + A));
				}
			}
		}
	}
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值