题面
题解(A*)
A*算法
在最小步数模型中,当状态数较多时,不能直接用BFS来做,我们就可以考虑A*算法来减少一些没必要的搜索状态
引入一个估值函数,用来估计某个点到达终点的距离。记f是估值函数,g是真实值,那么f(state) <= g(state),越接近越好 , 记dist是从起点到state状态的步数;利用的是优先队列,排序依据是dist[state] + f(state),当终点状态第一次出队时,就是最小步数。
本题
对于八数码问题,我们知道最终状态是序列12345678x (除x外不存在逆序对),对于每次交换改变的一定是偶数个逆序对,所以对于初始状态存在奇数逆序对时,那么此题一定无解
我们将现在的状态中每个数字到终点状态中每个数字的哈曼顿距离作为估计值一定小于等于真实值。
代码
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<unordered_map>
#include<queue>
#include<cmath>
using namespace std;
typedef pair<int, string> PIS;
int f(string state) {
int res = 0;
for (int i = 0; i < state.size(); i++) {
if (state[i] != 'x') {
int t = state[i] - '1';
res += abs(i / 3 - t / 3) + abs(i % 3 - t % 3);
}
}
return res;
}
string bfs(string start) {
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
char op[4] = {'u', 'r', 'd', 'l'};
string end = "12345678x";
unordered_map<string, int> dist;
//记录路径
unordered_map<string, pair<string, char>> prev;
//存放真实值+估计值 , 状态
priority_queue<PIS, vector<PIS>, greater<PIS>> heap;
dist[start] = 0;
heap.push({dist[start] + f(start), start});
while (heap.size()) {
auto t = heap.top();
heap.pop();
string state = t.second;
if (state == end) break;
int x, y;
for (int i = 0; i < state.size(); i++) {
if (state[i] == 'x') {
x = i / 3, y = i % 3;
break;
}
}
string source = state;
for (int i = 0; i < 4; i++) {
int nx = x + dx[i], ny = y + dy[i];
if (nx < 0 || nx > 2 || ny < 0 || ny > 2) continue;
swap(state[x * 3 + y], state[nx * 3 + ny]);
if (!dist.count(state) || dist[state] > dist[source] + 1) {
dist[state] = dist[source] + 1;
prev[state] = {source, op[i]};
heap.push({dist[state] + f(state), state});
}
swap(state[x * 3 + y], state[nx * 3 + ny]);
}
}
string res;
while (end != start) {
res += prev[end].second;
end = prev[end].first;
}
reverse(res.begin(), res.end());
return res;
}
int main() {
string start, seq, c;
for (int i = 0; i < 9; i++) {
cin>>c;
start += c;
if (c != "x") seq += c;
}
int cnt = 0;
for (int i = 0; i < 8; i++) {
for (int j = i; j < 8; j++) {
if (seq[i] > seq[j]) {
cnt++;
}
}
}
if (cnt & 1) cout << "unsolvable" << endl;
else cout << bfs(start) << endl;
return 0;
}