A星八数码

来源:互联网 发布:php简单的测试例子 编辑:程序博客网 时间:2024/05/16 10:13
#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