#include<iostream> #include<algorithm> using namespace std; struct node{ char num[9]; int gvalue; int hvalue; int fvalue; char action; node *parent; node *next; }; node *openlist,*closelist,*bestnode; char start[9]; char target[]={'1','2','3','4','5','6','7','8','x'}; bool isEmpty(node *p){ if(p->next==NULL) return true; return false; } bool isSolved(){ int s,t,i,j; s=t=0; for(i=0;i<9;i++) for(j=i;j>=0;j--){ if((start[i]<start[j])&&start[i]!='x'&&start[j]!='x') s++; if((target[i]<target[j])&&target[i]!='x'&&target[j]!='x') t++; } if((s%2)==(t%2)) return true; return false; } void print_result(node* p){ if(p!=NULL){ print_result(p->parent); cout<<p->action; } } int diffnum(node *p){ int i,count=0; for(i=0;i<9;i++) if(p->num[i]!=target[i]) count++; return count; } int findX(char num[]){ int i; for(i=0;i<9;i++) if(num[i]=='x') return i; } void move_up(node *&p){ int pos=findX(p->num); if(pos>2) {swap(p->num[pos],p->num[pos-3]);p->action='u';return;} return ; } void move_down(node *&p){ int pos=findX(p->num); if(pos<6) {swap(p->num[pos],p->num[pos+3]);p->action='d';return;} return; } void move_left(node *&p){ int pos=findX(p->num); if(pos!=0&&pos!=3&&pos!=6){swap(p->num[pos],p->num[pos-1]);p->action='l';return;} return; } void move_right(node *&p){ int pos=findX(p->num); if(pos!=2&&pos!=5&&pos!=8){swap(p->num[pos],p->num[pos+1]);p->action='r';return;} return; } void operate(node *&p,int op){ switch(op){ case 1:move_up(p);break; case 2:move_down(p);break; case 3:move_left(p);break; case 4:move_right(p);break; default:return; } } void expand(node* p){ int op,i; node* pNode; node* p1; for(op=1;op<=4;op++){ pNode=new node; for(i=0;i<9;i++) pNode->num[i]=p->num[i]; operate(pNode,op); pNode->gvalue=p->gvalue+1; pNode->hvalue=diffnum(pNode); pNode->fvalue=pNode->gvalue+pNode->hvalue; pNode->parent=p; pNode->next=NULL; if(bestnode==NULL) {bestnode=pNode;bestnode->next=NULL;} else {pNode->next=bestnode;bestnode=pNode;} } } struct node* openlist_insert(node *p){ node* temp=openlist; node* pre=openlist; if(temp==NULL) {openlist=p;openlist->next=NULL;return openlist;} while(temp!=NULL){ if(p->fvalue>temp->fvalue){ pre=temp; temp=temp->next; } else if(pre==temp){p->next=temp;return p;} else {pre->next=p;p->next=temp;return openlist;} } pre->next=p;p->next=NULL; return openlist; } struct node* Del(node* p,node* pList){ node* temp=pList; node* pre=pList; int i,count=0; while(temp!=NULL){ count=0; for(i=0;i<9;i++) if(p->num[i]==temp->num[i]) count++; if(count==9) { if(temp==pList) {pList=pList->next;delete temp;return pList;} else {pre->next=temp->next;delete temp;return pList;} } else pre=temp; temp=temp->next; } } struct node *InList(node *p,node *pList){ node *temp=pList; int i,k; while(temp!=NULL){ k=0; for(i=0;i<9;i++) if(p->num[i]==temp->num[i]) k++; if(k==9) return temp; temp=temp->next; } return NULL; } bool IsGoal(node* p){ int i; for(i=0;i<9;i++) if(p->num[i]!=target[i]) return false; return true; } void AStar(){ node *n=NULL,*m=NULL,*pNode=NULL; closelist=NULL; while(openlist!=NULL){ n=openlist; if(IsGoal(n)) {print_result(n);return;} openlist=openlist->next; n->next=closelist; closelist=n; expand(n); while(bestnode!=NULL){ m=bestnode; bestnode=bestnode->next; if(pNode=InList(m,openlist)){ if(m->fvalue<pNode->fvalue){ // pNode->fvalue=m->fvalue; openlist=Del(pNode,openlist); openlist=openlist_insert(m); } else delete m; } else if(pNode=InList(m,closelist)){ if(m->fvalue<pNode->fvalue){cout<<m->hvalue<<endl; closelist=Del(pNode,closelist); openlist=openlist_insert(m); }else delete m; } else openlist=openlist_insert(m); } } } int main(){ node *p=new node; openlist=new node; int i; for(i=0;i<9;i++) cin>>start[i]; for(i=0;i<9;i++) p->num[i]=start[i]; p->gvalue=0; p->hvalue=diffnum(p); p->fvalue=p->gvalue+p->hvalue; p->parent=NULL; p->next=NULL; openlist=p; if(isSolved()) AStar(); else cout<<"unsolved"<<endl; }
用组合数学的方法可以快速地进行判断,例如SOJ 2061题 2360题中都是关于此类的问题。但八数码的判断方法比他们简单多了。
本文用的方法是计算排列的逆序数值,以2 3 1 5 8 4 6 7 5 为例子,5表示的是空格,不计算,那么求23158467 的逆序值为
0 + 0 + 2 (1<2 1<3 ) + 0 + 0 + 2 ( 4<5 4<8) + 1 ( 6<8 ) + 1 ( 7<8 ) = 6
目标状态1 2 3 4 5 6 7 8 9 的逆序自然就是0。
两个状态之间是否可达,可以通过计算两者的逆序值,若两者奇偶性相同则可达,不然两个状态不可达。
简单证明一下:
l 和 r 操作,不会影响状态的逆序值,因为只会改变个位数(空格的位置)。
u和d操作是使某个位置的数字 右/左 移两位。 由于数字序列的每一次移动会使逆序值奇偶性改变,所以移动两次后奇偶性不变。
所以 四个操作均不会影响序列的奇偶性。
参考:http://blog.csdn.net/ray58750034/article/details/599897
http://blog.csdn.net/NowCan/article/details/256116