1.资料请参考刘汝佳的黑书,组合数学。
A*使用了h(s)做为在结点s下的代价下界值,转移时有h(s1)>=h(s2)+c(s1+s2),其中c(s1,s2)为状态s1转移到状态s2时所花费的代价!大家仔细揣摩一下这个方程,这个方程保证了A*算法的正确性(个人理解)
当s1,s2为相邻状态时有h(s1)>=h(s2)+1
然后用一个优先队列维护待扩展的结点,优先级判断的依据是f(s),f(s)=g(s)+h(s),g(s)表示从根结点到s结点花费的代价,h(s)为s结点的代价下界估计值。
个人感觉IDA*还好理解一些,它的核心思想就是用了一个变量pathLimit限制了搜索的深度,若成功则肯定是最优的,若不成功,则增加pathLimit直到有解!
2.A*实现
#include<cstdio> #include<cstring> #include<iostream> #include<queue> #include<cmath> using namespace std; int p[9]= {1,1,2,6,24,120,720,5040,40320}; int encode(int *perm)//n=9 { //八数码问题,九个格子,相当于总的排列数为9! int sum=0; for(int i=0; i<9; i++) { int count=0; for(int j=0; j<i; j++) { if(perm[j]>perm[i])count++; } sum+=count*p[9-perm[i]]; } return sum; } void decode(int sum,int *perm) { for(int i=0; i<9; i++)perm[i]=0; for(int i=1; i<=9; i++) { int tot=sum/p[9-i]+1; sum%=p[9-i]; int loc,count=0; for(loc=0; loc<9; loc++) { if(!perm[loc]) { count++; if(count==tot)break;//!!找到空第tot个空位置 } } perm[loc]=i; } } void initial(int *maze,int &space) { char grid; for(int i=0; i<9; i++) { cin>>grid; if(grid!='x')maze[i]=grid-'1'+1; else { space=i; maze[i]=9; } } } #define STATE 362890 struct Node { int preState; int step; int space; int h; } node[STATE]; bool vis[STATE]; bool isResolve(int *maze,int space) { int s=abs(double(2-space/3))+abs(double(2-space%3)); for(int i=0; i<9; i++) { for(int j=0; j<i; j++) { if(maze[i]>maze[j])s++; } } if(s&1)return false; else return true; } int h(int *maze) { int h=0; for(int i=0; i<9; i++) { if(maze[i]!=9)h+=abs(double((maze[i]-1)/3-i/3))+abs(double( (maze[i]-1)%3-i%3)); } return h; } int next[9][4]= { {-1,3,1,-1}, {0,4,2,-1}, {1,5,-1,-1}, {-1,6,4,0}, {3,7,5,1}, {4,8,-1,2}, {-1,-1,7,3}, {6,-1,8,4}, {7,-1,-1,5} }; class cmp { public: bool operator()(int sa,int sb) { return node[sa].h+node[sa].step>node[sb].h+node[sb].step; } }; bool AStar(int *maze,int space) { if(!isResolve(maze,space))return false; memset(vis,0,sizeof(vis)); priority_queue<int,vector<int>,cmp> que; int curState=encode(maze),nextState; node[curState].preState=-1; node[curState].step=0; node[curState].space=space; node[curState].h=h(maze); que.push(curState); while(!que.empty()) { curState=que.top(); que.pop(); if(!curState)break;//目标状态 if(vis[curState])continue; vis[curState]=true; decode(curState,maze); int cur=node[curState].space; for(int i=0; i<4; i++) { if(next[cur][i]==-1)continue; swap(maze[cur],maze[ next[cur][i] ] ); nextState=encode(maze); if(!vis[nextState] || node[nextState].step>node[curState].step+1) { node[nextState].preState=curState; node[nextState].space=next[cur][i]; node[nextState].step=node[curState].step+1; node[nextState].h=h(maze); que.push(nextState); } swap(maze[cur],maze[ next[cur][i] ] ); } } return true; } char dir[4]= { 'l','d','r','u' }; void output(int curState,int *maze) { decode(curState,maze); int preState=node[curState].preState; if(preState==-1)return; output(preState,maze); for(int i=0; i<4; i++) { if(next[node[preState].space][i]==node[curState].space) { cout<<dir[i]; break; } } } int main() { int maze[9],space; initial(maze,space); if(AStar(maze,space))output(0,maze); else cout<<"unsolvable"; cout<<endl; return 0; }
3.IDA*(其实这个算法比我们想像中的要简单得多,我猜大家直接看代码就能看懂!!!)
#include<cstdio> #include<cstring> #include<cmath> #include<iostream> using namespace std; void initial(int *maze,int &space) { char grid; for(int i=0; i<9; i++) { cin>>grid; if(grid!='x')maze[i]=grid-'1'+1; else { space=i; maze[i]=9; } } } bool isResolve(int *maze,int space) { int s=abs(double(2-space/3))+abs(double(2-space%3)); for(int i=0; i<9; i++) { for(int j=0; j<i; j++) { if(maze[i]>maze[j])s++; } } if(s&1)return false; else return true; } int h(int *maze) { int h=0; for(int i=0; i<9; i++) { if(maze[i]!=9) h+=abs(double((maze[i]-1)/3-i/3))+abs(double( (maze[i]-1)%3-i%3)); } return h; } int next[9][4]= { {-1,3,1,-1}, {0,4,2,-1}, {1,5,-1,-1}, {-1,6,4,0}, {3,7,5,1}, {4,8,-1,2}, {-1,-1,7,3}, {6,-1,8,4}, {7,-1,-1,5} }; bool isAns(int *maze) { for(int i=0; i<8; i++)if(maze[i+1]-maze[i]!=1)return false; return true; } int pathLimit; int path[362890],pathLen; bool IDAStar(int *maze,int len,int space) { if(len==pathLimit) { if(isAns(maze)) { pathLen=len; return true; } return false; } for(int i=0; i<4; i++) { if(next[space][i]!=-1) { if(len>0&&abs(double(i-path[len-1]))==2)continue;//!!不考虑相反的方向 swap(maze[space],maze[next[space][i]]); path[len]=i; if(h(maze)+len<=pathLimit&&IDAStar(maze,len+1,next[space][i])) return true; swap(maze[space],maze[next[space][i]]); } } return false; } char dir[4]= {'l','d','r','u'}; void output() { for(int i=0; i<pathLen; i++) { switch(path[i]) { case 0: cout<<'l'; break; case 1: cout<<'d'; break; case 2: cout<<'r'; break; case 3: cout<<'u'; } } } int main() { int maze[9],space; initial(maze,space); pathLimit=h(maze); if(isResolve(maze,space)) { while(!IDAStar(maze,0,space))pathLimit++; output(); } else cout<<"unsolvable"; cout<<endl; return 0; }