该题是一道经典的八数码问题,难点在于对状态的定义和判断该状态是否访问过。 紫书上列举了三种方法:set存,hash,编码解码。
编码解码的原理是利用了康托展开,我们都知道,对于一个长度为n的序列,其全排列一共有n!种,利用康托展开我们可以快速的求出一个排列是按照字典序的第几个排列。这样我们就将每个排列(状态)与一个n!内的整数对应起来了。 但是这样还是会超时,无奈我想到了既然终点状态固定,我们不妨从终点倒着BFS,实现打好表,记录下所有路径,这样就可以快速求出答案了。
细节参见代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
using namespace std;
typedef long long ll;
typedef int State[9];
const int maxstate = 10000000;
const int maxn = 1000;
State st[maxstate], goal;
int T,n,m,dist[maxstate], path[maxstate],id[maxstate];
const int dx[] = {1,0,-1,0};
const int dy[] = {0,1,0,-1};
char s[maxn],buf[maxn], hehe[maxstate],ch;
int vis[362885], fact[9];
void init_lookup_table() {
memset(vis,0,sizeof(vis));
fact[0] = 1;
for(int i=1;i<9;i++) fact[i] = fact[i-1] * i;
}
int try_to_insert(int s) {
int code = 0;
for(int i=0;i<9;i++) {
int cnt = 0;
for(int j=i+1;j<9;j++) if(st[s][j] < st[s][i]) cnt++;
code += fact[8-i] * cnt;
}
if(vis[code]) return 0;
id[code] = s;
return vis[code] = 1;
}
void print_path(int u) {
vector<char> ans;
while(u != 1) {
ans.push_back(hehe[u]);
u = path[u];
}
int len = ans.size();
for(int i=0;i<len;i++) {
if(ans[i] == 'u') printf("d");
else if(ans[i] == 'd') printf("u");
else if(ans[i] == 'l') printf("r");
else printf("l");
}
printf("\n");
}
void bfs() {
init_lookup_table();
int _front = 1, rear = 2;
try_to_insert(1);
dist[_front] = 0;
while(_front < rear) {
State& s = st[_front];
int z ;
for(z = 0; z < 9; z++) if(!s[z]) break;
int x = z/3, y = z%3;
for(int d = 0; d < 4; d++) {
int newx = x + dx[d];
int newy = y + dy[d];
int newz = newx * 3 + newy;
if(newx >= 0 && newx < 3 && newy >= 0 && newy < 3) {
State& t = st[rear];
memcpy(&t, &s, sizeof(s));
t[newz] = s[z];
t[z] = s[newz];
if(d == 0) hehe[rear] = 'd';
else if(d == 1) hehe[rear] = 'r';
else if(d == 2) hehe[rear] = 'u';
else hehe[rear] = 'l';
if(try_to_insert(rear)) {
dist[rear] = dist[_front] + 1;
path[rear] = _front;
rear++;
}
}
}
_front++;
}
return ;
}
int main() {
for(int i=1;i<=8;i++) goal[i-1] = i;
goal[8] = 0;
memcpy(st[1],goal,sizeof(goal));
bfs();
while(cin>>ch) {
if(ch == 'x') st[1][0] = 0;
else st[1][0] = ch - '0';
for(int i=1;i<9;i++) {
cin>>ch;
if(ch == 'x') st[1][i] = 0;
else st[1][i] = ch - '0';
}
int code = 0;
for(int i=0;i<9;i++) {
int cnt = 0;
for(int j=i+1;j<9;j++) if(st[1][j] < st[1][i]) cnt++;
code += fact[8-i] * cnt;
}
if(!vis[code]) printf("unsolvable\n");
else print_path(id[code]);
}
return 0;
}