1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 x
1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 5 6 7 8 5 6 7 8 5 6 7 8 5 6 7 8 9 x 10 12 9 10 x 12 9 10 11 12 9 10 11 12 13 14 11 15 13 14 11 15 13 14 x 15 13 14 15 x r-> d-> r->
2 3 4 1 5 x 7 6 8
ullddrurdllurdruldr
今年在北大暑期学校听讲时,郭老师讲过,但他的代码不能完全理解,特别是排列和序号相互转换。
此次再遇八数码问题,看到别的大神用康托展开计算序号,感觉十分方便,应该牢记。
但看到大神代码,优先队列以h为第一关键字,g为第二关键字能AC且450ms;
我自己本地跑,连样例都无法过,不过以f为第一关键字,h为第二关键字的方法比只以f为关键字的方法快大概200ms
#include <cstdio> #include <cstring> #include <string> #include <queue> using namespace std; struct Node { int a[9],pos,has,h,g; bool operator < (const Node& a) const { if(h+g!=a.h+a.g) return h+g>a.h+a.g; return h!=a.h?h>a.h:g>a.g; } }sta,u,v; inline bool up(const Node& t) { return t.pos>2; } inline bool right(const Node& t) { return t.pos!=2&&t.pos!=5&&t.pos!=8; } inline bool down(const Node& t) { return t.pos<6; } inline bool left(const Node& t) { return t.pos!=0&&t.pos!=3&&t.pos!=6; } bool (*tab[4])(const Node& t)={up,right,down,left}; int HASH[9]={40320,5040,720,120,24,6,2,1,1};//n的阶乘,变进制 int turn[400001]; int pre[400001]; const int des=46233; const int d[]={-3,1,3,-1}; const char m[]={"urdl"}; const int hs[9][9]={0,0,1,2,1,2,3,2,3,//位置hs[i][j]表示位置i数字为j时与正确位置的曼哈顿距离 0,1,0,1,2,1,2,3,2, 0,2,1,0,3,2,1,4,3, 0,1,2,3,0,1,2,1,2, 0,2,1,2,1,0,1,2,1, 0,3,2,1,2,1,0,3,2, 0,2,3,4,1,2,3,0,1, 0,3,2,3,2,1,2,1,0, 0,4,3,2,3,2,1,2,1}; bool judge() {//奇偶剪枝 int num=0,i,j; for(i=0;i<9;++i) for(j=i+1;j<9;++j) if(sta.a[i]&&sta.a[j]&&sta.a[i]>sta.a[j]) ++num; return (num&1)==1; } int get_hash(const Node& t) { int num=0,i,j,k; for(i=0;i<9;++i) { k=0; for(j=i+1;j<9;++j) if(t.a[i]>t.a[j]) ++k; num+=HASH[i]*k; } return num; } int get_h(const Node& t) {//计算估价函数h,曼哈顿距离和 int num=0; for(int i=0;i<9;++i) num+=hs[i][t.a[i]]; return num; } void astar() { int i; priority_queue<Node> q; q.push(sta); while(!q.empty()) { u=q.top(); q.pop(); for(i=0;i<4;++i) { if(tab[i](u)) { v=u; v.pos+=d[i]; swap(v.a[u.pos],v.a[v.pos]); if(turn[v.has=get_hash(v)]==-1) { turn[v.has]=i; ++v.g; v.h=get_h(v); pre[v.has]=u.has; q.push(v); } if(v.has==des) return ; } } } } void print() { string ans; int nxt=des; while(pre[nxt]!=-2) { ans.push_back(m[turn[nxt]]); nxt=pre[nxt]; } for(int i=ans.size()-1;i>=0;--i) printf("%c",ans[i]); printf("\n"); } int main() { //freopen("out.txt","w",stdout); int i; char s[3]; while(scanf("%s",s)==1) { if(s[0]=='x') { sta.a[0]=0; sta.pos=0; } else sta.a[0]=s[0]-'0'; for(i=1;i<9;++i) { scanf("%s",s); if(s[0]=='x') { sta.a[i]=0; sta.pos=i; } else sta.a[i]=s[0]-'0'; } if(judge()) { printf("unsolvable\n"); continue; } if((sta.has=get_hash(sta))==des) { printf("\n"); continue; } memset(turn,-1,sizeof(turn)); memset(pre,-1,sizeof(pre)); pre[sta.has]=-2; turn[sta.has]=0; sta.g=0,sta.h=get_h(sta); astar(); print(); } return 0; }