vlambda博客
学习文章列表

BFS练习(附带hash算法)

这个代码真的看的好久,不是我自己写的,但是陌生的东西太多了,孩怕啊。




#include<cstdio>

#include<cstring>

#include<set>

using namespace std; 

typedef int State[9];//定义“状态类型” 

const int maxstate=1000000; 

State st[maxstate],goal;//状态数组。所有状态都保存在这里 

int dist[maxstate]; //距离数组


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

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

//BFS,返回目标状态在st数组下标

//编码与解码 

set<int> vis;

void init_lookup_table()

{

vis.clear();

}

int try_to_insert(int s)

{

int v=0;

for(int i=0;i<9;i++)

v=v*10+st[s][i];

if(vis.count(v)) return 0;

vis.insert(v);

return 1;

}

int bfs()

{

init_lookup_table(); //初始化查找表

int front=1,rear=2;//不使用下标0,因为0被看作"不存在",front为队首指针,rear为队尾指针,因为要符合前闭后开的习惯 

while(front<rear)//队列不为空 

{

State& s=st[front]; //用“引用”来简化代码

if(memcmp(goal,s,sizeof(s))==0) return front;//找到目标状态,成功返回

int z;

for(z=0;z<9;z++) if(!s[z]) break;

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;

if(newx>=0&&newy<3&&newy>=0&&newx<3)//若移动合法 

{

State& t=st[rear]; //扩展新结点 

memcpy(&t,&s,sizeof(s));

t[newz]=s[z];

t[z]=s[newz];//交换移动的元素 

dist[rear]=dist[front]+1;//更新结点的距离值

if(try_to_insert(rear)) rear++;//如果成功插入查找表,修改队尾指针 

}

}

front++;

}

return 0;

}

int main()

{

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",dist[ans]);

else printf("-1\n");

return 0;

}

/*

编码与解码的另两种写法 

1.

int vis[362880],fact[9];

void init_lookup_table()

{

fact[0]=1;

for(int i=1;i<9;i++)

fact[i]=fact[i-1]*i;

}

int try_to_insert(int s)

{

int code=0;//st[s]映射到整数code

for(int i=0;i<9;i++)

{

int cnt;

for(int j=i+1;j<9;j++)

if(st[s][j]<st[s][i]) cnt++;

code+=fact[8-i]*cnt;

}

if(vis[code]) return 0;

return vis[code]=1;

}

2.hash技术

const int hashsize=1000003;

int head[hashsize],next[maxstate]={0};

void init_lookup_table()

{

memset(head,0,sizeof(head));

}

int hash(State &s)

{

int v=0;

for(int i=0;i<9;i++)

v=v*10+s[i];//把九个数字组成九位数 

return v%hashsize;//确保hash函数值是不超过hash表的大小的非负整数 

}

int try_to_insert(int s)

{

int h=hash(st[s]);//h记录编码值 

int u=head[h];

while(u)//若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;

}

*/



大概说一下几个要点

  1. dx,dy两个数组,分别模拟四个方向的走动,这个要好好学习,我记得之前我好像是用一堆堆判断来写的,实在蠢死了。

  2. 作者是用数组来模拟队列,队列模拟需要一个线性表和一个头指针,一个尾指针。

  3. hash算法:先将一段信息通过编码(这个必须是个双射)得到一个大整数,然后对大整数取模来缩小这个数据,此时有可能获得多段信息hash值相等,此时将hash值相等的所有信息连成链表,当遇到hash值相等的信息时,直接比较本来的信息,否则直接比较hash值即可判重。

  4. 还有引用的用法,这里不详细解释了




当你debug