BFS 八数码问题 typedef int State[9]; (BFS A*算法与优先队列)
来源:互联网 发布:百帝王啤酒知乎 编辑:程序博客网 时间:2024/05/21 10:38
题目描述
八数码问题,即在一个3×3的矩阵中有8个数(1至8)和一个空格,现在要你从一个状态转换到另一个状态,每次只能移动与空格相邻的一个数字到空格当中,问题是要你求从初始状态移动到目标状态所需的最少步数。如下图所示。
1
2
3
1
2
3
8
0
4
7
8
4
7
6
5
0
6
5
初始状态 目标状态
如上图所示的数据以矩阵形式给出。现在给出分别代表初始状态和目标状态的两个3*3的矩阵,请给出两个矩阵相互转化的最少步数。
输入
第1行-第3行:3个用空格分隔的整数,代表初始状态相应位置的数字,0代表空格
第4行-第6行:3个用空格分隔的整数,代表终止状态相应位置的数字,0代表空格
输出
第1行:一个整数,最小转换步数,如不能到相互转化则输出"Impossible"
样例输入
1 2 38 0 47 6 51 2 37 8 40 6 5
样例输出
2
可以通过BFS DFS来做
通过比较后发现我们采用BFS比较合理,因为DFS深度搜索下限不好设定,BFS按层搜索,只要不到目标状态就一直进行
如何表示状态呢?
将空格用0代替,然后9宫格成为了一串数字共有9!=362880
注意BFS的状态空间,有很多重复的状态,这时候就需要判重!!!
判重方法:
1)采用C++ STL中 set的特性,count来判断是否已经存在这样的状态,存在为1,不存在为0。但STL版本打时间效率最低
#include <cstdio>#include <cstring>#include <set>using namespace std;typedef int State[9];//State类型的const int MAXSTATE=1000000;State st[MAXSTATE],goal;// st为二维数组,goal为一维int dist[MAXSTATE]={0,0};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; //重复则为1,返回0 vis.insert(v); //不重复,插入并返回1 return 1;}const int dx[]={-1,1,0,0};const int dy[]={0,0,-1,1};int BFS(){ init_lookup_table();//初始化判重的set int front=1,rear=2; while(front<rear){ State &s=st[front]; //得到队头的9个数,组成的数组 if(memcmp(goal,s,sizeof(s))==0) return front; int z; for(z=0; z<9; z++) if(!s[z]) break; //寻找z,z为空格 int x=z/3,y=z%3; //将z从一维转换为二维 for(int d=0;d<4;d++){ //空格向四个方向扩展 int newx=x+dx[d]; int newy=y+dy[d]; int newz=newx*3+newy;//得到新的一维的z if(newx>=0&&newx<3&&newy>=0&&newy<3){// 移动合法 State & t=st[rear]; //得到队尾的9个数u,组成的数组 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\n",dist[ans]); else printf("Impossible\n"); return 0;}
2)哈希表,设计一个哈希函数,然后将任意结点Xx映射到某个给定范围[0,M-1],M是程序员根据内存大小自己选用的,理想状态下,只要一个M大的数组即可完成判重,但此时会有不同的结点的哈希值相同,这种情况下把哈希值相同的状态转换成链表。(可以理解为通过哈希函数建立了很多条链表,有的链表上只有一个节点,有的却有多个)
#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 MAXHASHSIZE = 1000003;int head[MAXHASHSIZE],next[MAXHASHSIZE];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%MAXHASHSIZE;}int try_to_insert(int s){ int h=hash(st[s]); //多条链表,进行定位是第几条链表 int u=head[h]; //链表的状态 0为链表为空 其他的则为已经创建进入循环判断 while(u){ //u=0链表为空则直接跳过,否则从表头开始查找链表,(针对于产生冲突的) if(memcmp(st[u],st[s],sizeof(st[s])==0)) //找到与之相同的,则插入失败 return 0; u=next[u]; //顺着链表继续查找 } next[s]=head[h]; //插入到链表当中, head[h]=s; return 1;}const int dx[]={-1,1,0,0};const int dy[]={0,0,-1,1};int BFS(){ init_lookup_table(); //初始化 int front=1,rear=2; //定义头尾 while(front<rear){ State &s =st[front]; //取队头的9宫格 if(memcmp(goal,s,sizeof(s))==0) //判断是否找到目标 return front; int z; for(z=0;z<9;z++) if(!s[z]) break; //寻找队头9宫格的空格 int x=z/3,y=z%3; //将一维的z转换成二维的 for(int d=0;d<4;d++) //向四个方向扩展 { int newx=x+dx[d]; int newy=y+dy[d]; int newz=newx*3+newy; //得到了扩展之后的z(空格) if(newx>=0&&newx<3&&newy>=0&&newy<3){ State &t=st[rear];//找个变量并且改变了尾指针指向的九宫格,下面把s的赋给了t 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++; //一个九宫格的扩展完成,出队列进行下一个 }}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\n", dist[ans]); else printf("-1\n"); return 0;}
参考《算法竞赛入门经典》 P199-202
代码值得学习的还有"引用"与"自定义",使得代码量减少
typedef int State[9];
State &s =st[front]; //取队头的9宫格
A*算法也可以应用到此题目,下面给出代码的思想:
这个题属于单个的起始状态到目标状态应用BFS,但是这里应用了A*算法+优先队列(BFS的优化 启发式算法),优先处理最有可能到达目标状态的9宫格
启发式算法的估价函数 1:
启发式算法的估价函数2:
#include <iostream>#include <cstdio>#include <cstring>#include <queue>#include <algorithm>#include <cmath>using namespace std;struct eight{ int st[9],bh; int step; //step表示步数 int g; //g表示从起始状态到当前状态的代价 int h; //h表示状态中不在位的方块数,若所有h=0,则为BFS int f; bool operator <(const eight &k)const{ if(f==k.f) return g>k.g; //若f值相同,g值小的优先 return f>k.f; //若f值不同,则f小的优先 }}s,t;priority_queue<eight> que;int goal[9];int visited[362885],fact[9];int ans;int dirs[4][2]={{-1,0},{1,0},{0,1},{0,-1}};int bianHao(int s[9]){ fact[0]=1; for(int i=1;i<9;i++) fact[i]=fact[i-1]*i; int code=0; for(int i=0;i<9;i++){ int cnt=0; for(int j=i+1;j<9;j++) if(s[j]<s[i]) cnt++; code+=fact[8-i]*cnt; } return code;}int wrongPlaceNum(int a[9]){ int cnt=0; for(int i=0;i<9;i++) if(a[i]!=goal[i]) cnt++; return cnt;}void Astar(){ eight t,t1; while(!que.empty()) { t=que.top(); que.pop();//取F值最小的队列元素 if(memcmp(goal,t.st,sizeof(t.st))==0){ans=t.step; break;} //找到目标状态返回 int z; for(z=0;z<9;z++) if(!t.st[z]) break; int x=z/3,y=z%3; for(int i=0;i<4;i++) { int newx=x+dirs[i][0]; int newy=y+dirs[i][1]; int newz=newz*3+newy; if(newx>=0&&newx<3&&newy>=0&&newx<3) { memcpy(&t1,&t,sizeof(t)); t1.st[newz]=t.st[z];//9宫格的状态的变化 t1.st[z]=t.st[newz]; t1.bh=bianHao(t1.st);//计算编号用于判重 if(!visited[t1.bh]) //r若状态t1未访问过 { t1.g=t.g+1; t1.h=wrongPlaceNum(s.st); t1.f=t1.g+t1.h; t1.step=t.step+1; que.push(t1);//压入优先队列 visited[t1.bh]=true;//标记 } } } }}int jopl(int s[9])//检查奇偶形{ int cnt,total=0; for(int i=0;i<9;i++) { cnt=0; if(s[i]==0) continue; //忽略空格 for(int j=0;j<i;j++) { if(s[j]==0) continue; //忽略空格 if(s[j]<s[i]) cnt++; } total += cnt; } return total%2;}int main(){ for(int i=0;i<9;i++) scanf("%d",&s.st[i]); for(int i=0;i<9;i++) scanf("%d",&goal[i]); if(jopl(s.st)^jopl(goal)) //若两状态的奇偶排列属性不同 { printf("Impossible\n"); return 0; } memset(visited,false,sizeof(visited)); s.g=s.step=0,s.bh=bianHao(s.st),s.h=wrongPlaceNum(s.st),s.f=s.g+s.h; while(!que.empty()) que.pop(); que.push(s); visited[s.bh]=true; ans=0; Astar(); printf("%d\n",ans); return 0;}
阅读全文
0 0
- BFS 八数码问题 typedef int State[9]; (BFS A*算法与优先队列)
- 八数码问题(typedef int state[9];)(多种解法)
- 八数码问题 bfs
- 八数码问题 BFS
- 八数码问题(A*&&双向BFS)
- 八数码问题 bfs 算法入门经典
- HDU(1043):八数码的 A* 与 双BFS算法
- 八数码(BFS)
- 图算法_普通广度优先搜索(BFS)解八数码问题_C语言
- 八数码问题(bfs+哈希)
- 八数码问题 (stl bfs)
- 简单的八数码问题(BFS)
- 八数码问题(hash+bfs)
- 八数码问题 bfs+map
- 八数码问题 BFS+hash
- 八数码问题 【隐式图bfs】
- POJ1077、HDU1043 Eight 八数码问题:双向BFS、A*
- hdu 1043 eight 八数码问题 bfs 和 A*
- 概率论:p(x|theta)和p(x;theta)的区别
- PLSQL是什么、为什么要用PLSQL
- python pip 装包出现 timeout error!
- TLC59401 的总结
- 程序员转型产品经理
- BFS 八数码问题 typedef int State[9]; (BFS A*算法与优先队列)
- 20170717
- 微信公众平台 多客服解决开启服务端失效方法
- JavaWeb学习总结(四十七)——监听器(Listener)在开发中的应用
- git小白学习之路
- JavaScript基础的一些小总结
- http://blog.csdn.net/wemedia/details.html?id=40237
- JavaWeb学习总结(四十八)——模拟Servlet3.0使用注解的方式配置Servlet
- https 建立连接过程