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;
}