题目描述
在一个3×3的网格中,1~8这8个数字和一个“X”恰好不重不漏地分布在这3×3的网格中。
在游戏过程中,可以把“X”与其上、下、左、右四个方向之一的数字交换(如果存在)。
我们的目的是通过交换,使得网格变为如下排列(称为正确排列):
例如,示例中图形就可以通过让“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
思路
先进行可解性判定。,把除空格之外的所有数字排成一个序列, 求出该序列的逆序对数。如果初态和终态的逆序对数奇偶性相同,那么这两个状态互相可达,否则一定不可达。
int sum = 0;
for(int i = 0; i < 8; i++)
for(int j = i + 1; j < 8; j++)
if(s[i] > s[j]) sum++;
if(sum & 1) cout << "unsolvable" << endl;
我们就采用A*算法搜索种移动步数最少的方案。每次移动只能把一 个数字与空格交换位置从任何个状态到目标状态的移动步数也不可能小于所有数字当前位置与目标位置的曼哈顿距离之和。
求曼哈顿距离
int f(string s){
int ans = 0;
for(int i = 0; i < 9; i++){
if(s[i] != 'x'){
int t = s[i] - '1';
ans += abs(i /3 - t / 3) + abs(i % 3 - t % 3);
}
}
return ans;
}
于是,对于任意状态state,我们可以把估价函数设计为所有数字在state 中的位置与目标状态end中的位置的曼哈顿距离之和。
代码
#include
#include
#include
#include
#include
#include
using namespace std;
typedef pair<int, string> PIS;
string start, s, c;
char p[4] = {'l', 'r', 'u', 'd'};
int dx[4] = {0, 0, -1, 1};
int dy[4] = {-1, 1, 0, 0};
int f(string s){
int ans = 0;
for(int i = 0; i < 9; i++){
if(s[i] != 'x'){
int t = s[i] - '1';
ans += abs(i /3 - t / 3) + abs(i % 3 - t % 3);
}
}
return ans;
}
string bfs(){
priority_queue<PIS, vector<PIS>, greater<PIS> > q;
map<string, int> dis;
map<string, bool> vis;
map<string, pair<string, char> > pre;
q.push({f(start), start});
dis[start] = 0;
while(q.size()){
PIS t = q.top();
q.pop();
string stare = t.second;
if(stare == "12345678x") break;
if(vis[stare]) continue;
vis[stare] = true;
int x, y;
for(int i = 0; i < 9; i++){
if(stare[i] == 'x'){
x = i / 3;
y = i % 3;
break;
}
}
int step = dis[stare];
string raw = stare;
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){
stare = raw;
swap(stare[x * 3 + y], stare[a * 3 + b]);
if(!dis.count(stare) || dis[stare] > step + 1){
dis[stare] = step + 1;
pre[stare] = {raw, p[i]};
q.push({f(stare) + dis[stare], stare});
}
}
}
}
string ans, end = "12345678x";
while(end != start){
ans += pre[end].second;
end = pre[end].first;
}
reverse(ans.begin(), ans.end());
return ans;
}
int main(){
for(int i = 0; i < 9; i++){
cin >> c;
start += c;
if(c != "x") s += c;
}
int sum = 0;
for(int i = 0; i < 8; i++)
for(int j = i + 1; j < 8; j++)
if(s[i] > s[j]) sum++;
if(sum & 1) cout << "unsolvable" << endl;
else cout << bfs() << endl;
return 0;
}