题目地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=217
首先说一下八数码问题的可解性。
1.互换2个非零位置,状态的逆序奇偶性将保持不变。
2.互换0和另一个非零位置,状态的逆序奇偶性将发生颠倒。
3.因此,如果目标状态和起始状态的逆序奇偶性相同,问题可解,否则不可解。所以对于八数码问题的一个目标状态有9!/2种可解状态
逆序奇偶性可以这样来求,将状态转化了数列,然后去掉X位,然后对于每一位,计算在它之前小于它的个数,每一位对应的值之和就是逆序奇偶数。
A*算法具体不多说了,它的精髓在于f(n)=g(n)+h(n),中估值函数h(n)的设计。值的注意的是:
如果估价值h(n)<= n(到目标节点的实际步长),这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。
如果 估价值>实际值, 搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。
因此在实际使用的时候应该更具需要来设计h(n)
在zoj_1217中,为了不TLE,选择h(n)=20*dis(欧几里德距离),但是得不到最优解
zoj_1217:
#include<cstdio> #include<algorithm> #include<queue> #include<string.h> #include<string> #include<math.h> #include<iostream> using namespace std; int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; string path[4]={"d","r","u","l"}; int dp[10]={1,1,2,6,24,120,720,5040,40320,362880}; int final[9]={1,2,3,4,5,6,7,8,0}; bool hashtable[362885]; class state { public: int a[9],exp,zero,dept; string path; state(){}; state(int num[9]) { for(int i=0;i<9;++i) a[i]=num[i]; } void swapn(int c) { swap(a[zero],a[c]); } }; struct cmp { bool operator()(const state x,const state y) { return x.exp>y.exp; } }; int getHash(int code[9]) { int sum=0; bool flag[9]; int k; memset(flag,false,sizeof(flag)); for(int i=0;i<9;++i) { k=0; for(int j=code[i]+1;j<9;++j) { if(!flag[j]) ++k; } sum+=k*dp[8-i]; flag[code[i]]=true; } return 362879-sum; } int Eudis(int a[9]) { int dis=0; for(int i=0;i<9;++i) { for(int j=0;j<9;++j) { if(a[i]==final[j]) { dis+=fabs(1.0*(i/3-j/3))+fabs(1.0*(i%3-j%3)); break; } } } return dis/2; } state ans; bool bfs(state ori) { priority_queue<state,vector<state>,cmp >open; //queue<state>open; open.push(ori); while(!open.empty()) { state t=open.top(); open.pop(); if(Eudis(t.a)==0) { ans=t; return true; } for(int i=0;i<4;++i) { int row=t.zero/3; int col=t.zero%3; int arow=row+next[i][1]; int acol=col+next[i][0]; if(arow<3&&arow>=0&&acol<3&&acol>=0) { int sw=arow*3+acol; state tmp=t; tmp.swapn(sw); tmp.zero=sw; int hcode=getHash(tmp.a); if(hashtable[hcode]) continue; hashtable[hcode]=true; int dis=50*Eudis(tmp.a); ++tmp.dept; tmp.exp=dis+tmp.dept; tmp.path+=path[i]; open.push(tmp); } } } return false; } void test(bool flag[9],int b,int num[9]) { if(b>8) { printf("%d\n",getHash(num)); return; } for(int i=0;i<9;++i) { if(!flag[i]) { flag[i]=true; num[b]=i; test(flag,b+1,num); flag[i]=false; } } } bool isSolveable (int statue[9]) { int num=0; for (int i=1;i<=8;++i) { if(statue[i]==0) continue; for(int j=0;j<i;++j) { if(statue[j]==0) continue; if(statue[i]>statue[j]) ++num; } } if (num%2==1) return false; return true; } int main() { //bool ff[9]; //int nn[9]; //memset(ff,false,sizeof(ff)); //memset(nn,false,sizeof(nn)); //test(ff,0,nn); //int a[9]={0,1,2,3,4,5,6,7,8}; //int b[9]={8,7,6,5,4,3,2,1,0}; //printf("%d\n",getHash(b)); freopen("e:\\zoj\\zoj_1217.txt","r",stdin); while(1) { memset(hashtable,0,sizeof(hashtable)); int flag=0; int num[9]; int zero; char c; for(int i=0;i<9;++i) { if(scanf(" %c",&c)==EOF) return 0; if(c=='x') { zero=flag; num[flag++]=0; } if(c<='9'&&c>='1') num[flag++]=c-48; if(flag>=9) break; } state ori(num); ori.exp=20*Eudis(num); ori.zero=zero; ori.dept=0; int hcode=getHash(num); hashtable[hcode]=true; if(!isSolveable(num)) printf("unsolvable\n"); else { bool f=bfs(ori); int l=ans.path.length(); for(int i=0;i<l;++i) printf("%c",ans.path[i]); puts(""); } } }
双向BFS:
#include<cstdio> #include<algorithm> #include<queue> #include<string.h> #include<string> #include<iostream> using namespace std; int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; string path[4]={"d","r","u","l"}; string path2[4]={"u","l","d","r"}; int dp[10]={1,1,2,6,24,120,720,5040,40320,362880}; int final[9]={1,2,3,4,5,6,7,8,0}; int hashtable[362885]; class state { public: int a[9],zero; string path; state(){}; state(int num[9]) { for(int i=0;i<9;++i) a[i]=num[i]; } void swapn(int c) { swap(a[zero],a[c]); } }; state x,y; state sl[362885]; int getHash(int code[9]) { int sum=0; bool flag[9]; int k; memset(flag,false,sizeof(flag)); for(int i=0;i<9;++i) { k=0; for(int j=code[i]+1;j<9;++j) { if(!flag[j]) ++k; } sum+=k*dp[8-i]; flag[code[i]]=true; } return 362879-sum; } bool check(state a,state b) { bool flag=true; for(int i=0;i<9;++i) { if(a.a[i]!=b.a[i]) { flag=false; break; } } return flag; } bool bfs(state ori,state fin) { queue<state>open,open2; open.push(ori); open2.push(fin); int hcode,fcode; state tmp,tmp2,t1,t2; while(1) { if(!open.empty()) { t1=open.front(); open.pop(); for(int i=0;i<4;++i) { int row=t1.zero/3; int col=t1.zero%3; int arow=row+next[i][1]; int acol=col+next[i][0]; if(arow<3&&arow>=0&&acol<3&&acol>=0) { int sw=arow*3+acol; tmp=t1; tmp.swapn(sw); tmp.zero=sw; tmp.path+=path[i]; hcode=getHash(tmp.a); if(hashtable[hcode]==1) continue; if(hashtable[hcode]==2) { x=tmp; y=sl[hcode]; return true; } hashtable[hcode]=1; sl[hcode]=tmp; open.push(tmp); } } } if(!open2.empty()) { t2=open2.front(); open2.pop(); for(int i=0;i<4;++i) { int row=t2.zero/3; int col=t2.zero%3; int arow=row+next[i][1]; int acol=col+next[i][0]; if(arow<3&&arow>=0&&acol<3&&acol>=0) { int sw=arow*3+acol; tmp2=t2; tmp2.swapn(sw); tmp2.zero=sw; tmp2.path+=path2[i]; fcode=getHash(tmp2.a); if(hashtable[fcode]==2) continue; if(hashtable[fcode]==1) { y=tmp2; x=sl[fcode]; return true; } hashtable[fcode]=2; sl[fcode]=tmp2; open2.push(tmp2); } } } } return false; } bool isSolveable (int statue[9]) { int num=0; for (int i=1;i<=8;++i) { if(statue[i]==0) continue; for(int j=0;j<i;++j) { if(statue[j]==0) continue; if(statue[i]>statue[j]) ++num; } } if (num%2==1) return false; return true; } int main() { freopen("e:\\zoj\\zoj_1217.txt","r",stdin); while(1) { memset(hashtable,0,sizeof(hashtable)); int flag=0; int num[9]; int zero; char c; for(int i=0;i<9;++i) { if(scanf(" %c",&c)==EOF) return 0; if(c=='x') { zero=flag; num[flag++]=0; } if(c<='9'&&c>='1') num[flag++]=c-48; if(flag>=9) break; } state ori(num); ori.zero=zero; state fin(final); fin.zero=8; int ocode=getHash(num); int fcode=getHash(final); hashtable[ocode]=1; hashtable[fcode]=2; sl[ocode]=ori; sl[fcode]=fin; if(!isSolveable(num)) printf("unsolvable\n"); else { if(check(ori,fin)) puts(""); bool f=bfs(ori,fin); int l=x.path.length(); for(int i=0;i<l;++i) printf("%c",x.path[i]); l=y.path.length(); for(int i=l-1;i>=0;--i) printf("%c",y.path[i]); puts(""); } } }