八数码问题 数组模拟队列bfs 链表哈希涵数判重

在BFS的时候使用链表实现的哈希表记录vis判重

sample input:

2 6 4
1 3 7
0 5 8

8 1 5
7 3 6
4 0 2
 sample output:

31

无法到达目标局面输出 -1

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

typedef int State[9];
const int maxstate=1000000;
State st[maxstate],goal;
//State是一个int[9]的类型     int st[maxstate][9];
int dist[maxstate];

const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};

const int hashsize=1000003;
int head[hashsize], next[maxstate];

void init_lookup_table(){ memset(head,0,sizeof(head)); }

int hash_f(State &s){
    int v=0;
    for(int i=0;i<9;i++) v=v*10+s[i];       //将9个数字的位置组合成一个9位数
    return v % hashsize;         //确保hash函数值是不超过hash表大小的非负整数
}

int try_to_insert(int s){                  //尝试插入hash表要把hash值相同的状态组织成链表
    int h=hash_f(st[s]);                   //获取该结点的hash值
    int u=head[h];                         //从表头开始查找链表
    while(u){                               //存在哈希冲突
        if(memcmp(st[u],st[s],sizeof(st[s]))==0)return 0; //找到了,插入失败,该结点已被扩展
        u=next[u];                          //顺着链表继续找
    }
    next[s]=head[h];                       //插入到链表中
    head[h]=s;                             //更新表头
    return 1;
}

int bfs(){
    init_lookup_table();
    int frnt=1, rear=2;                    //不使用下标0,因为0被看做不存在
    while(frnt<rear){
        State& s=st[frnt];
        if(memcmp(goal,s,sizeof(s))==0) return frnt; //找到目标状态,成功返回
        int z;
        for(z=0;z<9;z++) if(!s[z])break;   //z是 "0" 的位置
        int x=z/3,y=z%3;
        for(int d=0;d<4;d++){
            int newx=x+dx[d];
            int newy=y+dy[d];
            int newz=newx*3+newy;         //进行移动后 "0" 的新位置
            if(newx>=0&&newx<3&&newy>=0&&newy<3){  
                State& t=st[rear];
                memcpy(&t,&s,sizeof(s));  //扩展新结点
                t[newz]=s[z];
                t[z]=s[newz];            //移动操作即为将 "0" 和目标方块互换
                dist[rear]=dist[frnt]+1; //更新新结点的距离值
                if(try_to_insert(rear))rear++;  //插入hash表成功,修改队尾指针
            }
        }
        frnt++;                           //该父结点的子节点扩展完毕后修改队首指针
    }
    return 0;                            //bfs失败,无法到达目标状态
}

int main (){
    //freopen("datain.txt","r",stdin);
    for(int i=0;i<9;i++)scanf("%d",&st[1][i]);
    for(int i=0;i<9;i++) scanf("%d",&goal[i]);
    int ans=bfs();                               //返回目标状态的编号
    if(ans>0)printf("%d\n",dist[ans]);           
    else printf("-1\n");                         //返回0,则无法到达目标状态
    return 0;
}

猜你喜欢

转载自blog.csdn.net/hanker99/article/details/85233010