【反向BFS+康托展开】Eight HDU - 1043
题意:八数码问题
思路:反向BFS+康托展开
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;
//注意全排列数组a是从零开始的
}
void decantor(int x, int*ans) {//x哈希值,n数字个数,a算出的全排列
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 hash;
int pos;
int pre;
};
string path[maxn];//下标是一个状态的哈希值,表示从该状态到达目标状态的路径
bool vis[maxn];
int dx[] = { 1,-1,0,0 };
int dy[] = { 0,0,-1,1 };
char dir_op[5] = "udrl";
//反向bfs,所以路径记录和实际运算反着来,如空格坐标运算为下移一行时,路径记录为u
void bfs() {
queue<node> q;
node temp;
int a[10];
for (int i = 0; i < 9; i++) a[i] = i + 1;
//反向bfs
//故目标状态即起始状态,即123456789
temp.hash = cantor(a);
temp.pre = -1;
temp.pos = 8;//从0起计,代表空格的9在状态数组中下标为8
path[temp.hash] = "";
q.push(temp);
vis[temp.hash] = 1;
while (!q.empty()) {
temp = q.front();
q.pop();
int sx = temp.pos / 3;
int sy = temp.pos % 3;
decantor(temp.hash, a);
//逆运算得到该节点状态数组
for (int i = 0; i < 4; i++) {
int nx = sx + dx[i];
int ny = sy + dy[i];
int npos = nx * 3 + ny;
//换位后空格所在的新位置
if (nx < 0 || ny < 0 || nx>2 || ny>2) continue;
swap(a[temp.pos], a[npos]);
//换位置后的状态数组再算出哈希值
int nval = cantor(a);
if (vis[nval]) {
swap(a[temp.pos], a[npos]);
//如果该状态已经遇到过,复原回去,继续尝试其他换位
continue;
}
node t;
t.hash = nval;
t.pos = npos;
t.pre = temp.hash;
//记录该状态的上一状态的哈希值
path[nval] = dir_op[i] + path[temp.hash];
//注意因为是反向bfs,移动记录的顺序也是反着的
//如从某状态经udlr到达状态123456789
//而代码从123456789开始运算,是经lrud到达该状态的
//其中“方向相反”已在定义dir_op时完成,只要将新的操作记在之前所有操作的最前面即可
q.push(t);
vis[nval] = 1;
//新状态入队并记录
swap(a[temp.pos], a[npos]);
//复原回去,继续尝试其他换位
}
}
}
int main()
{
//ios::sync_with_stdio(false);
//int t; cin >> t; while (t--){
string s;
memset(vis, 0, sizeof(vis));
bfs();
//bfs一次解决所有多组样例
int a[10];
while (getline(cin, s)) {
int j = 0;
for (int i = 0; i < s.size(); i++) {
if (s[i] == 'x') a[j++] = 9;
else if (isdigit(s[i])) a[j++] = s[i] - '0';
}
int ans = cantor(a);
if (vis[ans]) {
cout << path[ans] << endl;
}
else cout << "unsolvable" << endl;
}
return 0;
}