AcWing 179 八数码 题解(搜索—A*搜索)

AcWing 179 八数码
A*算法应用,缩减搜索范围,达到提高搜索效率的效果。需要注意的是,入堆的是预估距离(这里是曼哈顿距离)+实际距离,另外需要记住的就是利用堆去记录上一步的状态和命令序列的操作

#include<bits/stdc++.h>

using namespace std;

#define x first
#define y second

typedef pair<int, string>PSI;

int f(string s){  //曼哈顿距离,字符串到达标准字符串需要的最小距离 
	int res = 0;
	for(int i = 0; i < s.size(); i ++ ){
		if(s[i] != 'x'){
			int j = s[i] - '1';  //得出这个数字是几 
			res = res + abs(i / 3 - j / 3) + abs(i % 3 - j % 3);  //曼哈顿距离,字符串到达标准字符串需要的最小距离 
		}
	}
	return res;
}

string bfs(string start){
	string end = "12345678x";  //标准状态 
	priority_queue<PSI, vector<PSI>, greater<PSI>>heap;   //A*堆 
	unordered_map<string, int>dist;  //储存字符串和对应的距离 
	unordered_map<string, pair<string, char>>pre;  //储存上一个状态 
	
	heap.push({f(start), start});  //将起点和起点的预估值加入堆中 
	dist[start] = 0;
	
	int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
	char dc[4] = {'u', 'r', 'd', 'l'};
	
	while(heap.size()){
		auto op = heap.top();
		heap.pop();
		
		string state = op.y;  //字符串 
		if(state == end) break;
		
		int d = dist[state];  //实际距离 
		int xx, yy;
		for(int i = 0; i < state.size(); i ++ ){
			if(state[i] == 'x'){
				xx = i / 3;
				yy = i % 3;
				break;
			}
		}
		string init = state;
		for(int i = 0; i < 4; i ++ ){
			int a = xx + dx[i], b = yy + dy[i];  //交换后的位置 
			if(a >= 0 && a < 3 && b >= 0 && b < 3) {  //如果交换后的位置合法 
				swap(state[xx * 3 + yy], state[a * 3 + b]);   //交换 
				if(!dist.count(state) || dist[state] > d + 1){  //如果这个字符串状态没有出现过,或找到这个状态的更小距离 
					dist[state] = d + 1;  //记录距离 
					pre[state] = {init, dc[i]};  //记录这个状态是由哪一个状态转移来,由哪个操作而来 
					heap.push({f(state) + dist[state], state});  //新的字符串入堆,实际距离+预估距离入堆用于排序 
				}
				swap(state[xx * 3 + yy], state[a * 3 + b]);  //因为要扩展四个方向,所以要返回原来字符串状态			
			}
			
		}
	}
	
	//记录操作 
	string res;
	while(end != start){
		res += pre[end].y;  //记录操作 
		end = pre[end].x;  //字符串返回上一个状态 
	}
	reverse(res.begin(), res.end());  //因为是逆着找操作,所以命令序列要倒序 
	return res;
}

int main()
{
	
	string s, start, g;
	char l;
	while(cin>>l){
		start += l;
		if(l != 'x') g += l;
	}
	int ni = 0;  //记录逆序对数 
	for(int i = 0; i < 8; i ++ ){
		for(int j = i + 1; j < 8; j ++ ){
			if(g[i] > g[j]){
				ni ++ ;	
			}
		}
	}
	
	if(ni % 2) cout<<"unsolvable"<<endl;
	else bfs(start);
	
	return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值