题面
在一个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
思路
直接bfs就可以了,这里由于我们需要把操作给记录下来,那么我们就需要沿着路线进行一个回溯,那么我们可能需要去保存当前状态的一个前状态和操作。用map可以解决。A*的做法的话我们需要一个有限队列并且加入估价函数,这里的估价函数就是所有乱序数字曼哈顿距离之和。
代码实现
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<unordered_map>
#include<string>
using namespace std;
char change[5]="drul";
int moves[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
string target="12345678x";
unordered_map <string, string> p2;
unordered_map <string, int > p1;
inline int bfs (string s) {
queue <string> q;
q.push (s);
p1[s]=0;
while (q.size ()) {
string sh=q.front ();
string h=sh;
q.pop ();
if (sh==target) return true;
int k=sh.find (‘x‘);
int x=k/3,y=k%3;
for (int i=0;i<4;i++) {
int a=x+moves[i][0];
int b=y+moves[i][1];
if (a>=0&&a<3&&b>=0&&b<3) {
int t=a*3+b;
swap (sh[k],sh[t]);
if (!p1.count (sh)) {
p1[sh]=i;
p2[sh]=h;
q.push (sh);
}
swap (sh[t],sh[k]);
}
}
}
return false;
}
int main () {
string s;
char c;
for (int i=0;i<9;i++) cin>>c, s+=c;
if (bfs (s)) {
vector <char> arr;
string ans=target;
while (ans!=s) {
arr.push_back (change[p1[ans]]);
ans=p2[ans];
}
for (int i=arr.size ()-1;i>=0;i--) {
cout<<arr[i];
}
cout<<endl;
}
else cout<<"unsolvable"<<endl;
return 0;
}