题意:八数码问题,给定初始状态与目标状态,输出字典序最小的移动路径和步数。
思路:
-
双向BFS:
以初始状态和目标状态为两个起点,同时出发,汇合时即答案
但需要注意,从初始状态出发的正向BFS所得路径必是字典序最小的(前提是方向遍历是按字典序从小到大);从目标状态出发的反向BFS则不一定,当反向BFS遇到之前搜索过的反向状态时,需要比较新的路径字典序和已有的路径字典序的大小。
以及需要把棋盘信息存进结构体里面,以省去逆康托展开的步骤,否则会TLE
#include<iostream>
#include<stdlib.h>
#include<time.h>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<cstring>
#include<cstdio>
#include <string>
/*#define mem(a,b) memset(a,b,sizeof(a))
#define f(i,a,b) for(int i=a;i<=b;i++)
#define af(i,a,b) for(int i=a;i>=b;i--)
*/
using namespace std;
typedef long long LL;
const LL INF = 0x3f3f3f3f;
const int maxn = 362885;
const int FAC[] = { 1,1,2,6,24,120,720,5040,40320,362880,3628800 };
int cantor(int* a) {//算出全排列对应的哈希值
int x = 0;
for (int i = 0; i < 9; i++) {
int smaller = 0;
for (int j = i + 1; j < 9; j++) {
if (a[j] < a[i]) smaller++;
}
x += FAC[9 - i - 1] * smaller;
}
return x+1;
//注意全排列数组a是从零开始的
}
/*
void decantor(int x,int*ans) {//x哈希值,n数字个数,a算出的全排列
x--;
vector<int> v;
for (int i = 1; i <= 9; i++) v.push_back(i);
for (int i = 0; i < 9; i++) {
int r;
r = x / FAC[9 - i - 1];
x = x % FAC[9 - i - 1];
sort(v.begin(), v.end());
ans[i] = v[r];
v.erase(v.begin() + r);
}
//注意算出的全排列数组ans是从0开始的
}
*/
//描述每个状态需要有以下信息
struct node {
int a[15];
int hash;
int pos;
int deep;
};
string path[maxn];
int vis_go[maxn];
int vis_back[maxn];
int dx[] = { 1,0,0,-1 };
int dy[] = { 0,-1,1,0 };
char go_op[] = "dlru";
//正向的记录
//要求字典序最小,所以在尝试的时候也从小到大尝试
char back_op[] = "urld";
//反向的记录
queue<node> q_go;//正向的队列
queue<node> q_back;//反向的队列
void bfs(node begin,node end) {
if (begin.hash == end.hash) {
cout << 0 << endl << endl;
return;
}
q_go.push(begin);
vis_go[begin.hash] = 1;
path[begin.hash] = "";
q_back.push(end);
vis_back[end.hash] = 1;
path[end.hash] = "";
int L = 0;
while (!q_go.empty() || !q_back.empty()) {
while (!q_go.empty() && q_go.front().deep == L) {
node tmp = q_go.front();
q_go.pop();
for (int i = 0; i < 4; i++) {
int nx = tmp.pos / 3 + dx[i];
int ny = tmp.pos % 3 + dy[i];
int npos = nx * 3 + ny;
if (nx < 0 || ny < 0 || nx>2 || ny>2) continue;
node now=tmp;
swap(now.a[tmp.pos], now.a[npos]);
now.hash = cantor(now.a);
now.pos = npos;
now.deep = tmp.deep + 1;
if (vis_back[now.hash]) {
//正向,进行本次操作之后变成了反向搜索过的状态
//此时正反汇合了,路径已贯通
cout << (int)path[now.hash].size() + (int)path[tmp.hash].size() + 1 << endl;
//+1是本次操作的一次
reverse(path[now.hash].begin(), path[now.hash].end());
//此时path[now.hash]储存的是反向搜索的路径,但由于记录是正向的,这里要反转字符串
cout << path[tmp.hash] << go_op[i] << path[now.hash] << endl;
//先输出正向搜索的路径,以及本次的操作,然后再输出反向搜索的路径
return;
}
if (vis_go[now.hash]) continue;
//正向,进行本次操作后变成了正向搜索过的状态,就跳过
q_go.push(now);
vis_go[now.hash] = 1;
path[now.hash] = path[tmp.hash] + go_op[i];
//cout << "go:"<<path[now.hash] << endl;
}
}
while (!q_back.empty() && q_back.front().deep == L) {
node tmp = q_back.front();
q_back.pop();
for (int i = 0; i < 4; i++) {
int nx = tmp.pos / 3 + dx[i];
int ny = tmp.pos % 3 + dy[i];
int npos = nx * 3 + ny;
if (nx < 0 || ny < 0 || nx>2 || ny>2) continue;
node now=tmp;
swap(now.a[tmp.pos], now.a[npos]);
now.hash = cantor(now.a);
now.pos = npos;
now.deep = tmp.deep + 1;
if (vis_go[now.hash]) continue;
//反向,进行本次操作后遇到了正向搜索过的状态,跳过
if (vis_back[now.hash] && path[now.hash].size() && path[now.hash][(int)path[now.hash].size() - 1] < back_op[i]) continue;
//如果遇到了反向搜索过的状态
//比较已有路径的最后一个字母和本次操作的字母大小
//如果本次操作字母更大,说明替换后新的路径字典序更大,不能替换
//否则用新的路径替换掉原有路径
q_back.push(now);
vis_back[now.hash] = 1;
path[now.hash] = path[tmp.hash] + back_op[i];
//cout << "back:" << path[now.hash] << endl;
}
}
L++;
//两个队列进行下一层搜索
}
}
void clear() {
while (!q_go.empty()) q_go.pop();
while (!q_back.empty()) q_back.pop();
memset(vis_go, 0, sizeof(vis_go));
memset(vis_back, 0, sizeof(vis_back));
}
int main()
{
ios::sync_with_stdio(false);
int t; cin >> t; getchar();// while (t--) {
for (int k = 1; k <= t; k++) {
clear();
string s;
node temp[3];
for (int i = 1; i <= 2; i++) {
//初始状态和目标状态入队,双向BFS
getline(cin, s);
int j = 0;
for (int p = 0; p < s.size(); p++) {
if (s[p] == 'X') {
temp[i].a[j++] = 9;
temp[i].pos = j - 1;
}
else if (isdigit(s[p])) temp[i].a[j++] = s[p] - '0';
}
temp[i].hash = cantor(temp[i].a);
temp[i].deep = 0;
}
cout << "Case " << k << ": ";
bfs(temp[1], temp[2]);
}
return 0;
}
}