八数码

来源:互联网 发布:华师大网络教育 编辑:程序博客网 时间:2024/04/29 06:27
#include<iostream>#include<cmath>  //为用abs多花了15个长度(本人喜欢“缩减”(减短长度)代码)using namespace std;string qs[1000001],data[1000001],s,c,start,gz[9]={"13","240","51","046","1573","284","37","486","57"};  //qs用来放变化队列,而data用来判重。s用来在每一次交换数字的时候判重并判断是否已经是结果了。c是放队列的头。start放起始状态,gz放九个格子分别相邻的地方。int sum,list[1000001]={0},next[1000001]={0},u,tim[1000001],cnt=0,t,x,p;char tmp;  //sum list next u cnt跟哈希有关,tim、t放步数,x p tmp用来交换。int hash(string h)  //哈希{    sum=0;    for(int i=0;i<h.size();i++)sum=sum*33+h[i];    sum=abs(sum%1000007),u=list[sum];  //你不加ABS会RE(反正我是这样)    while(u){if(data[u]==h)return 0;u=next[u];}    data[++cnt]=h,next[cnt]=list[sum],list[sum]=cnt;    return 1;}  //用链表做哈希int bfs()  //广搜上场。{    int head=0,tail=1;    while(head<tail)    {        head++,c=qs[head],t=tim[head],x=c.find("0",0);  //先找到空格位置。        for(int i=0;i<gz[x].size();i++)  //进行交换        {            s=c,p=gz[x][i]-48;  //开始找交换位置            tmp=s[x],s[x]=s[p],s[p]=tmp;  //开始交换            if(s=="123804765"){cout<<t+1;return 0;}  //交换完毕后如果结束,则退出            if(hash(s))tail++,qs[tail]=s,tim[tail]=t+1;}  //如果没有重复就入队    }}int main(){    cin>>start;    if(start=="123804765"){cout<<"0";return 0;}  //如果一开始就达到目标就直接结束。    qs[1]=start,tim[1]=0,bfs();  //否则开始宽搜}

A*:个人认为A*比双向宽搜好写的多,代码长度也是很短,估价函数非常容易想到,即当前所有1-8(不包括0)数字位置与目标位置的距离之和,在这里可能会出现一个疑问:为什么是不包括0的?我刚开始也是打上0,的仔细研究之后可以发现答案都偏大2左右(其实把Ans-2也可以A了数据太水)后来发现:只要1-8的数字都完美地和目标位置匹配了,那0这个可以自由移动的数字也自然在目标位置上了,故可以估价的时候不统计(在这里可以不把0看成一个需要转移到目标位置上的数字,而是不存在的缺口,可能会有助于理解)接下来可以迭代枚举答案,每次记录0坐标即可,若 当前估价值+当前步数>答案 则剪枝(因为估价值是一个完成匹配的最低限)。

#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>using namespace std;struct A{int x,y;};int a[3][3],flag,stx,sty,Ans;int p1[4]={1,-1,0,0};int p2[4]={0,0,1,-1};int gx[9]={1,0,0,0,1,2,2,2,1};int gy[9]={1,0,1,2,2,2,1,0,0};int Guess(){    A z[9]={0};int sum=0;    for(int i=0;i<=2;i++)        for(int j=0;j<=2;j++)            z[a[i][j]].x=i,z[a[i][j]].y=j;    for(int i=1;i<9;i++)        sum+=(abs(z[i].x-gx[i])+abs(z[i].y-gy[i]));    return sum;}void Dfs(int now,int goal,int x,int y){    if(now>goal)return;    if(flag)return;int v=Guess();    if(v==0){flag=1;return;}    if(now+v>goal)return;    for(int k=0;k<4;k++){        int nx=x+p1[k];int ny=y+p2[k];        if(nx<0||nx>2||ny<0||ny>2)continue;        swap(a[x][y],a[nx][ny]);        Dfs(now+1,goal,nx,ny);        swap(a[x][y],a[nx][ny]);    }}int main(){    char ch[10];scanf("%s",ch);    int k=0;    for(int i=0;i<3;i++)    for(int j=0;j<3;j++){        a[i][j]=ch[k]-'0',k++;        if(a[i][j]==0)stx=i,sty=j;    }Ans=-1;    while(!flag)        Ans++,Dfs(0,Ans,stx,sty);    printf("%d\n",Ans);    return 0;}

)

2 0
原创粉丝点击