P1379 八数码难题
P1379 八数码难题
大意
每个棋子上标有 1 至 8 的某一数字。棋盘中留有一个空格,空格用 0 来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局和目标布局 \(123804765\)。
思路
这个题目要求我们把给出的状态通过类似华容道的方式,去移动 \(0\) 的位置,然后就可以调整其他数的位置。
然后我们考虑把这个棋盘的状态存下来,这样的话我们就可以直接转移,然后我们显然可以把这玩意直接弄成字符串,为了保证答案的最小,直接字符串 \(hash\) 存每个字符串的最小值,然后我们每次关注 \(0\) 的位置,进行 bfs 即可,新的状态取最小即可。这种已经可以 AC了。
我们继续考虑优化,这里给出 A* 的写法,我们是否有很多没必要的状态呢?A* 的剪枝就很类似于贪心的思路,我们可以维护一个东西,每次在 bfs 的时候取你觉得比较优的答案。这时候我们需要维护一个估价函数,对于这个题的话,我们可以认为,位置正确的越多,那么这个东西显然很优,先对这些正确位置比较多的进行拓展,那么我们有 \(F(S)\) 就是表示当前的状态下正确的位置的个数,然后我们去维护一个小根堆,里面的值是 \(S_{now} + F(S)\),这个东西越小显然越优,我们优先去拓展,如果已经到了目标状态,那么就没必要继续搜索了,这个答案一定最优。这个东西就很类似最短路的 dijkstra,每次都取最小的。然后显然我们的 \(F(S)\) 是要小于等于实际的可能花费的,因此这样的拓展一定最优。
代码
#include<iostream>
#include<cmath>
#include<unordered_map>
#include<queue>
using namespace std;
string s;
int xx[] = {1, 0, 0, 0, 1, 2, 2, 2, 1};
int yy[] = {1, 0, 1, 2, 2, 2, 1, 0, 0};
int dx[] = {1, 0, -1, 0};
int dy[] = {0, 1, 0, -1};
int f(string now){
int sum = 0;
for(int i = 0;i < 9;i ++){
int x = i / 3, y = i % 3;
int p = x * 3 + y;
int v = now[p] - '0';
sum += (abs(x - xx[v]) + abs(y - yy[v]));
}
return sum;
}
int Astar(string now){
unordered_map<string, int> d;
priority_queue<pair<int, string> > q;
string tag = "123804765";
q.push(make_pair(-f(now), now));
d[now] = 0;
while(!q.empty()){
string u = q.top().second;
if(u == tag) return d[tag];
q.pop();
int k = u.find('0');
int x = k / 3, y = k % 3;
for(int i = 0;i < 4;i ++){
int nx = x + dx[i], ny = y + dy[i];
if(nx < 0 || ny < 0 || nx >= 3 || ny >= 3) continue;
if(nx * 3 + ny > 8 || nx * 3 + ny < 0) continue;
string t = u;
swap(t[x * 3 + y], t[nx * 3 + ny]);
if(!d.count(t)){
d[t] = d[u] + 1;
q.push(make_pair(-f(t) - d[t], t));
}
}
}
return d[tag];
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin >> s;
cout << Astar(s) << '\n';
return 0;
}
本文来自一名高中生,作者:To_Carpe_Diem

浙公网安备 33010602011771号