题目描述
在一个 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。
样例
in:
2 3 4 1 5 x 7 6 8
out:
ullddrurdllurdruldr
算法
A*(优化搜索) +map(记忆路径)
A*::优先队列+估计函数(最优路径假设)
priority_queue<> q;
while(q.size()) {
t <- 取队头;
if(t==ans) break;
for(遍历边) {
更新边,将{新状态的估计值,新状态}入队
}
}
代码
#include<bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef pair<int,string> P;
int f(string m)//估计函数
{
int dt=0;
for(int i=0;i<9;i++)
if(m[i]!='x')
{
int t=m[i]-'1';
dt=dt+abs(i/3-t/3)+abs(i%3-t%3);
}
return dt;
}
string bfs(string start)
{
string end="12345678x";
unordered_map<string,int> d;
priority_queue<P, vector<P>, greater<P>> heap;
unordered_map<string,pair<string,char>> last;
heap.push({f(start),start});
d[start]=0;
char oper[]="uldr";
int dx[4]={-1,0,1,0},dy[4]={0,-1,0,1};
//A*
while(heap.size())
{
auto t=heap.top();
heap.pop();
string state=t.y;
if(t.y==end) break;
int x,y;
for(int i=0;i<9;i++)
if(state[i]=='x')
{
x=i/3,y=i%3;
break;
}
string init=state;
for(int i=0;i<4;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a<0||a>=3||b<0||b>=3) continue;
swap(state[a*3+b],state[x*3+y]);
if(!d.count(state)||d[state]>d[init]+1)
{
d[state]=d[init]+1;
heap.push({f(state)+d[state],state});
last[state]={init,oper[i]};
}
state=init;
}
}
//map记忆路径回溯
string ans;
while(end!=start)
{
ans+=last[end].y;
end=last[end].x;
}
reverse(ans.begin(),ans.end());
return ans;
}
int main()
{
string start,x,c;
while(cin>>c)
{
start+=c;
if(c!="x") x+=c;
}
int res=0;
for(int i=0;i<8;i++)
for(int j=i+1;j<8;j++)
if(x[i]>x[j])
res++;
if(res%2) printf("unsolvable\n");
else cout<<bfs(start)<<endl;
return 0;
}