原题链接
在一个3×3的网格中,1~8这8个数字和一个“X”恰好不重不漏地分布在这3×3的网格中。
例如:
1 2 3
X 4 6
7 5 8
在游戏过程中,可以把“X”与其上、下、左、右四个方向之一的数字交换(如果存在)。
我们的目的是通过交换,使得网格变为如下排列(称为正确排列):
1 2 3
4 5 6
7 8 X
例如,示例中图形就可以通过让“X”先后与右、下、右三个方向的数字交换成功得到正确排列。
交换过程如下:
1 2 3
1 2 3
1 2 3
1 2 3
X 4 6
4 X 6
4 5 6
4 5 6
7 5 8
7 5 8
7 X 8
7 8 X
把“X”与上下左右方向数字交换的行动记录为“u”、“d”、“l”、“r”
。
现在,给你一个初始网格,请你通过最少的移动次数,得到正确排列。
输入格式
输入占一行,将3×3的初始网格描绘出来。
例如,如果初始网格如下所示:
1 2 3
x 4 6
7 5 8
则输入为:1 2 3 x 4 6 7 5 8
输出格式
输出占一行,包含一个字符串,表示得到正确排列的完整行动记录。如果答案不唯一,输出任意一种合法方案即可。
如果不存在解决方案,则输出”unsolvable”
。
输入样例:
2 3 4 1 5 x 7 6 8
输出样例
ullddrurdllurdruldr
A star算法是一直沿着某条估计出来的路劲搜索的,且估计出来的路劲一定要小于等于实际路劲
A star的形式与Dijkstra的形式非常接近(Dijkstra估计函数为0),但是Dijkstra在每次出队之后就已经定下来出队点已是最小距离,而A star不行。A star只能保证终点状态是最小的。
八数码问题若序列中的逆序对数量为奇数个则无法走到终点
这里估价函数采用曼哈顿距离,为理想状态每个位置(如‘1’ 在(1,1)上)到终点位置('1'的终点坐标(0,0))的曼哈顿距离(则此时'1'的曼哈顿距离为2)之和。这样的算出来的距离一定小于等于实际上该状态到终点的最小距离
#include
#include
#include
#include
#include
using namespace std;
int dx[] = {
-1,0,1,0};
int dy[] = {
0,1,0,-1};
char op[5] = "urdl";
int f(string state)
{
//曼哈顿估价函数,估价值为每个位置离终点的曼哈顿距离之和
int res = 0;
for(int i = 0; i < 9; i++)
if(state[i] != 'x')
{
int t = state[i] - '1';
res += abs(t/3 - i/3) + abs(t%3 - i%3);
}
return res;
}
void bfs(string start)
{
string end = "12345678x";
unordered_map<string,int> dist; //存真实距离
unordered_map<string,pair<char,string>>pre; //存路径
priority_queue<pair<int,string>,vector<pair<int,string> >,greater<pair<int,string> > > heap;//存离终点的估计距离
dist[start] = 0;
heap.push({
dist[start] + f(start),start});
while(heap.size())
{
auto t = heap.top();
heap.pop();
string state = t.second , source = t.second;
int distance = t.first;
if(state == end)
{
//到达终点,输出路径
string path;
while(start != end)
{
path += pre[end].first;
end = pre[end].second;
}
reverse(path.begin(),path.end());
cout << path << endl;
return;
}
int location;
for(int i = 0; i < 9; i++)
if(state[i] == 'x') location = i;//找到'x'的位置
for(int i = 0; i < 4; i++)
{
//向四个方向扩展
state = source;
int x = (location / 3) + dx[i];
int y = (location % 3) + dy[i];
swap(state[location],state[x*3 + y]);
if(x < 0 || x >= 3 || y < 0 || y >= 3) continue;
if(dist.count(state) == 0 || dist[state] > dist[source] + 1)
{
// 如果是第一次扩展到这个状态 或 走到这个状态有更小的步数
dist[state] = dist[source] + 1;
heap.push({
dist[state] + f(state),state});
pre[state] = {
op[i],source};
}
}
}
}
int main()
{
string start;
for(int i = 0; i < 9;i++)
{
char c;
cin >> c;
start += c;
}
int cnt = 0;
for(int i = 0; i < 9; i++)
for(int j = i; j < 9; j++)
if(start[i] != 'x' && start[j] != 'x' && start[i] > start[j])
cnt++;
if(cnt & 1) puts("unsolvable");
else bfs(start);
return 0;
}