这一题跟八数码的区别就是一个是A*算法一个是IDA*算法,本质都是差不多的,但是还有一些细节需要去考虑。
1、估价函数:跟八数码一眼的,只需要知道每个符号最终的位置就行了,所以首先预处理一下每个字符的位置。然后在查找某个状态的估价值的时候,就是只需要找到0~8的所有数字到达最终位置的曼哈顿距离就行,这些曼哈顿距离的和就是估价值,0~8位置正确,那么X肯定是正确的位置。
2、优化:每个位置有四个方向,其中有一个是来当前位置的方向,要是回去就相当于是无效操作,那么则可以进行忽略无效操作的优化。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N = 1e3;
typedef long long ll;
typedef pair<int, int> pii;
int maxi, idx, dx[] = {1, 0, 0, -1}, dy[] = {0, -1, 1, 0};
string ed, be, p = "dlru";// 方向坐标是按字典序的,这样可以使找到的最小步数的字典序也是同步数最小的情况。
pii o[300];
char str[N];
int f(string a) // 估价函数,就是每个字符位置跟最终位置的曼哈顿距离,也就是当前字符要走到最终位置需要的最小步数。
{
int s = 0;
for (int i = 0; i < 9; i ++)
if(a[i] != 'X') // 如果0~8都正确了,那X自然也就正确了。
s += abs(i / 3 - o[a[i]].first) + abs(i % 3 - o[a[i]].second);
return s;
}
int pp[N];
bool dfs(int x)
{
if(x + f(be) > maxi) return false; // 当前深度加上最小估价 还大于目标步数,则直接结束
if(be == ed) return true; // 到达终点 直接结束 返回状态
int a, b;
for(int i = 0; i < 9; i ++) // 获取X的位置的横纵坐标
if(be[i] == 'X')
{
a = i / 3, b = i % 3;
break;
}
for(int i = 0; i < 4; i ++) // 枚举方向
{
int a1 = a + dx[i], b1 = b + dy[i]; // X移动后的坐标
if(x && i + pp[x - 1] == 3) continue; // 优化:当天操作相当于回到上一个位置,那就跳过
if(a1 < 0 || a1 > 2 || b1 < 0 || b1 > 2) continue; // 超出地图 跳过
swap(be[a * 3 + b], be[a1 * 3 + b1]); // 移动X的位置
str[x] = p[i]; // 记录当前行进方向
pp[x] = i; // 记录行进方向下标,用来判断是否是回到原地
if(dfs(x + 1)) // 向更深一层搜索
return true;
swap(be[a * 3 + b], be[a1 * 3 + b1]); // 还原场地
}
return false;
}
int main(void)
{
IOS;
int t, g = 1;
cin >> t;
while (t --)
{
cin >> be >> ed;
idx = 0;
for(int i = 0; i < 9; i ++) // 预处理,保存一下每个字符最终的位置。
o[ed[i]] = {i / 3, i % 3};
cout << "Case " << g ++ << ": ";
for(maxi = 0; ; maxi ++) // IDA* 思想
if(dfs(0))
break;
cout << maxi << endl;
for(int i = 0 ; i < maxi; i ++) cout << str[i];
cout << endl;
}
return 0;
}