题目描述
在 3×3 的棋盘上,摆有八个棋子,每个棋子上标有 11 至 88 的某一数字。棋盘中留有一个空格,空格用 00 来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为 123804765123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变
输入格式
输入初始状态,一行九个数字,空格用 00 表示。
输出格式
只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数。保证测试数据中无特殊无法到达目标状态数据。
总结如下:
迭代加深的A*算法
所谓迭代加深就是每次限制搜索深度, 这样可以在整个搜索树深度很大而答案深度又很小的情况下大大提高效率
使k从1开始不断加深枚举, 作为最大步数进行迭代加深搜索判断,而对于不用移动的情况可以一开始直接特判
在这里我们的A*估价函数设置为
当前状态还有多少个位置与目标状态不对应
若当前步数+估价函数值>枚举的最大步数 则直接返回
当然这只是基本思路,搜索还可以有很大优化
我们在搜索中再加入最优性剪枝, 显然当前枚举下一个状态时如果回到上一个状态肯定不是最优, 所以我们在枚举下一状态时加入对这种情况的判断
将状态数组对称排列会很方便进行这一操作。
我的ac代码:
#include<iostream>
#include<unordered_map>
#include<queue>
#include<cstring>
using namespace std;
string s;
unordered_map<string, int>dist;
int dx[] = { 0,0,-1,1 };
int dy[] = { -1,1,0,0 };
int bfs(string s) {
queue<string>q;
q.push(s);
string end = "123804765";
while (!q.empty()) {
auto t = q.front();
q.pop();
if (t == end)return dist[t];
int pos = t.find('0');
int x = pos / 3;
int y = pos % 3;
for (int i = 0; i < 4; i++) {
int a = x + dx[i];
int b = y + dy[i];
if (a < 0 || a >= 3 || b < 0 || b >= 3)continue;
string str = t;
swap(str[pos], str[a * 3 + b]);
if (!dist.count(str)) {
dist[str] = dist[t] + 1;
q.push(str);
}
}
}
}
int main() {
/*for (int i = 0; i < 9; i++) {
char ch;
cin >> ch;
s += ch;
}*/
cin >> s;
dist[s] = 0;
int ans = bfs(s);
cout << ans << endl;
return 0;
}