HDU1043、以及POJ1077上面都有这道题目,可以说是搜索里的非常经典的题目了。
poj上面的数据真的是弱,由于只有一组数据,简单bfs直接就可以过掉。
前前后后捣鼓了能有6个小时,才把这道题目在HDU上以4500ms的微弱优势通过。。。。。。。
我的思路一开始是简单的bfs,在HDU上超时。然后改用A*搜索,尝试了3种启发函数,才微弱的过掉。。。。我看别人的代码也是A*算法,而且启发函数写的跟我一样,为什么就几百ms过掉了呢。。。。还是不明白。。。。
思路:
(1)本题比较关键的一点就是判重问题,怎么样保证同一个状态只能被访问一次,涉及到状态的表示问题。
而状态其实是一个排列数,我们现在想要把一个排列数Hash到一个整数上,这就用到了康托展开的方法。(见我前一个博文)
(2)第二个比较关键的地方就是启发函数的选取
启发函数设置了两个参数:
val:当前状态到目标状态的哈密顿距离。
step: 当前已经移动的次数。
函数1:val + step :在poj上以67ms通过,在HDU上TLE
函数2:step为第一关键字,val为第二关键字 在poj上以760ms通过,在HDU当然TLE
函数3:以val为第一关键字,以step为第二关键字,在poj上以0ms通过,在HDU上以4500ms通过
这里还有一个需要注意的地方,就是无解情况的判定:
有一个定理,当两个状态的逆序数的奇偶性相同的时候,他们可以互相到达。
否则,他们无法互相到达,这个定理可以快速完成无解判定。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
class cantor{
public:
int fac_dp[11];
int fac(int i){
if(fac_dp[i]) return fac_dp[i];
return fac_dp[i] = i*fac(i-1);
}
void init(){
memset(fac_dp,0,sizeof(fac_dp));
fac_dp[0] = 1;
fac(10);
}
int encode(int num[],int n){
int ans = 0;
for(int i = 0;i < n;i++){
int cnt = 0;
for(int j = i;j < n;j++){
if(num[i] > num[j])
cnt++;
}
ans += fac_dp[n-i-1] * cnt;
}
return ans;
}
void decode(int ans[],int num,int n){
int used[11];
for(int i = 0;i < n;i++) used[i] = 0;
for(int i = 0;i < n;i++){
int cnt = num/fac_dp[n-i-1];
int r = num%fac_dp[n-i-1];
for(int j = 0;j < n;j++){
if(!cnt && !used[j]){
used[j] = 1;
ans[i] = j;
break;
}
if(!used[j]){
cnt--;
}
}
num = r;
}
}
};
struct node{
int key;
int val;
int step;
int loc;
int x[9];
friend bool operator<(node n1,node n2){
if(n1.val == n2.val){
return n1.step > n2.step;
}
return n1.val > n2.val;
}
node(int a,int b,int c,int loc):key(a),val(b),step(c),loc(loc){
}
};
int a[11],b[11];
const int MAX = 3628800;
int pre[MAX];
int preid[MAX];
int visited[MAX];
int dx[4] = {1,-1,0,0};
int dy[4] = {0,0,-1,1};
int tarmp[9] = {8,0,1,2,3,4,5,6,7};
char dc[4] = {'d','u','l','r'};
cantor ct;
void prtpath(int end,int beg){
char stk[100];
int cnt = 0;
while(end != beg){
stk[cnt++] = dc[pre[end]];
end = preid[end];
}
while(cnt){
putchar(stk[--cnt]);
}
puts("");
}
bool check(int arr[]){
int sum = 0;
for(int i = 0;i < 9;i++){
if(arr[i] == 0) continue;
for(int j = i;j < 9;j++){
if(arr[j] == 0) continue;
if(arr[i] > arr[j]) sum++;
}
}
return sum%2 == 0;
}
int calc(int arr[]){
int ans = 0;
for(int i = 0;i < 9;i++){
ans += abs(i/3 - tarmp[arr[i]]/3) + abs(i%3 - (tarmp[arr[i]]%3));
}
return ans;
}
int main(){
ct.init();
char c;
while(~scanf(" %c",&c)){
memset(visited,0,sizeof(visited));
memset(pre,0,sizeof(pre));
if(c == 'x') a[0] = 0;
else a[0] = c - '0';
for(int i = 1;i < 9;i++){
scanf(" %c",&c);
if(c == 'x'){
a[i] = 0;
}
else{
a[i] = c - '0';
}
}
if(!check(a)){
puts("unsolvable");
continue;
}
for(int i = 0;i < 8;i++) b[i] = i+1;
b[8] = 0;
int tar = ct.encode(b,9);
int code = ct.encode(a,9);
preid[code] = code;
priority_queue<node> Q;
int lc = -1;
while(a[++lc] != 0);
node cur = node(code,calc(a),0,lc);
for(int i = 0;i < 9;i++) cur.x[i] = a[i];
Q.push(cur);
visited[code] = 1;
int f = 0;
while(!Q.empty()){
node nn = Q.top();Q.pop();
int ccd = nn.key;
if(ccd == tar){
//找到了!
//cout<<"YES"<<endl;
prtpath(tar,code);
f = 1;
break;
}
int lc = nn.loc;
int x = lc / 3;
int y = lc % 3;
nn.step ++;
for(int i = 0;i < 4;i++){
int nx = dx[i] + x;
int ny = dy[i] + y;
if(nx >= 0 && nx < 3 && ny >= 0 && ny < 3){
int nlc = nx * 3 + ny;
swap(nn.x[lc],nn.x[nlc]);
int ncd = ct.encode(nn.x,9);
if(!visited[ncd]){
pre[ncd] = i;
preid[ncd] = ccd;
nn.key = ncd;
nn.val = calc(nn.x);
nn.loc = nlc;
Q.push(nn);
visited[ncd] = 1;
}
swap(nn.x[lc],nn.x[nlc]);
}
}
}
if(!f){
puts("unsolvable");
}
}
}