路径寻找问题:八数码问题
来源:互联网 发布:java项目部署到tomcat 编辑:程序博客网 时间:2024/06/08 18:23
在现实问题中,很多问题都可以归节为图的遍历,但这些问题中的图却不是事先给定,从程序读入的,而是有程序动态生成的,称为隐式图。路径寻找问题可以归结为隐式图的遍历,你的任务是找到一条从初始状态到终止状态的最优路径。下面我们来看路径寻找问题的经典问题:八数码问题。
题意:如下如所示,a,b分别表示两种状态,每个九宫格中的数字都是0~8共9个数字的任意一种排列,现在要把算出a状态移动到b状态的最佳步骤(移动步数最少)。移动的规则是0方块与上下左右任意一块互换位置。其实这个问题和我们经常玩的拼图游戏是一样的。0就代表空。
题目链接:https://vjudge.net/problem/HIT-1868
分析:不难把八数码问题归结为图上的最短路问题。无权图上的最短路问题都可以用bfs求解。直接暴搜,虽然效率不高,但是可以求出结果。但是存在一个问题,就是bfs的判重问题。对于图来说,搜索的时候一定要标记已经访问过的结点,刚开始我的想法是用set集合存储一个字符串(比如上面左图字符串为238016745),访问过的结点都将其放到集合里,每次判重的时候只需要访问set,看里面有没有这个结点即可,但是这个问题本来就存在效率问题,STL效率将会更低。看了刘汝佳老师的算法竞赛入门经典。我又学到了一些方法来解决这个问题。
下面我将几种方法代码贴下面。几种标记方法原理可参见:http://blog.csdn.net/mylinchi/article/details/53714235,这位大牛写的很清楚,我就不详细介绍了。
1.设计一套排列的编码和解码问题。
#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>#include<vector>using namespace std;typedef int state[9];const int maxstate=600000;const int INF=0x7fffffff;const int maxn=30;const int dir[4][2]= {{1,0},{-1,0},{0,1},{0,-1}};state st[maxstate],goal;int dist[maxstate];int vis[maxstate];int fact[10];int head,rear;void init() { fact[0]=1; for(int i=1; i<9; i++) fact[i]=fact[i-1]*i;//求阶乘}int try_insert(int s) { //判断结点是否访问 int sum=0; for(int i=0; i<9; i++) { int cnt=0; for(int j=i+1; j<9; j++) if(st[s][j]<st[s][i]) cnt++; sum+=fact[8-i]*cnt; } if(vis[sum]) return 0; vis[sum]=1; return 1;}int bfs() { while(head<rear) { state& s=st[head]; if(memcmp(goal,s,sizeof(s))==0) return head; int z; for(z=0; z<9; z++) if(!s[z]) break; int x=z/3,y=z%3; for(int i=0; i<4; i++) { int new_x=x+dir[i][0]; int new_y=y+dir[i][1]; int new_z=new_x*3+new_y; if(new_x<0||new_x>=3||new_y<0||new_y>=3) continue; state& t=st[rear]; memcpy(&t,&s,sizeof(s)); t[new_z]=s[z]; t[z]=s[new_z]; if(try_insert(rear)) { dist[rear]=dist[head]+1; rear++; } } head++; } return 0;}int main() { //freopen("in.txt","r",stdin); int T; cin>>T; init(); while(T--) { for(int i=0; i<9; i++) cin>>st[1][i]; for(int i=0; i<9; i++) cin>>goal[i]; head=1;rear=2; memset(vis,0,sizeof(vis)); memset(dist,0,sizeof(dist)); if(bfs()) cout<<dist[head]<<endl; else cout<<-1<<endl; } return 0;}
2.使用哈希技术,设计哈希函数。
#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>#include<vector>using namespace std;typedef int state[9];const int maxstate=600000;const int INF=0x7fffffff;const int maxn=30;const int hashsize = 1000003;const int dir[4][2]= {{1,0},{-1,0},{0,1},{0,-1}};state st[maxstate],goal;int dist[maxstate];int fact[10];int head,rear;int myhead[hashsize];int mynext[maxstate];void init() { memset(myhead, 0, sizeof(myhead)); memset(mynext, 0, sizeof(mynext));}int myhash(int src) { int val = 0; for (int i = 0; i < 9; i++) val = 10 * val + st[src][i]; return val%hashsize;}bool try_insert(int s) { int hcode = myhash(s); int u = myhead[hcode]; while (u) { if (memcmp(st[u],st[s],sizeof(st[s])) == 0) return false; u = mynext[u]; } mynext[s] = myhead[hcode]; myhead[hcode] = s; return true;}int bfs() { init(); while(head<rear) { state& s=st[head]; if(memcmp(goal,s,sizeof(s))==0) return head; int z; for(z=0; z<9; z++) if(!s[z]) break; int x=z/3,y=z%3; for(int i=0; i<4; i++) { int new_x=x+dir[i][0]; int new_y=y+dir[i][1]; int new_z=new_x*3+new_y; if(new_x<0||new_x>=3||new_y<0||new_y>=3) continue; state& t=st[rear]; memcpy(&t,&s,sizeof(s)); t[new_z]=s[z]; t[z]=s[new_z]; if(try_insert(rear)) { dist[rear]=dist[head]+1; rear++; } } head++; } return 0;}int main() { //freopen("in.txt","r",stdin); int T; cin>>T; while(T--) { for(int i=0; i<9; i++) cin>>st[1][i]; for(int i=0; i<9; i++) cin>>goal[i]; head=1;rear=2; memset(dist,0,sizeof(dist)); if(bfs()) cout<<dist[head]<<endl; else cout<<-1<<endl; } return 0;}
不过可惜,这两种写法都超时,鉴于自己水平有限,读者课参考下面链接AC代码。
http://blog.csdn.net/niuox/article/details/8960833
- 路径寻找问题:八数码问题
- 八数码——路径寻找问题
- 八数码路径寻找算法
- 八数码问题
- [人工智能]八数码问题
- 八数码问题.....
- 八数码问题
- 八数码问题分析
- 八数码问题
- 人工智能-八数码问题
- 八数码问题
- 八数码问题,Puzzle
- 八数码问题
- 八数码问题
- 八数码问题
- 八数码问题源代码
- 八数码问题
- 八数码问题
- 第一天
- 进程控制常用的一些操作
- 密码破译问题:地球和天女星座开战了,你是地球防卫军的密码破解员,以下你是破解出来的部分密码 tyt:好 huy:这 bvn:天 jkl :是 lgc:你 qqq:哈
- 内核编译不能找到standby.code错误解决办法
- python中的 while True
- 路径寻找问题:八数码问题
- opencv在CLion下CMakeList配置
- 想转型成为用户体验设计师,先看看这些再说
- 【SpringCloud】(十):高可用 Eureka
- 将SM2加密库从vs2008移植到vs2015上时报错
- R中的条形图和饼图绘制
- 水平垂直居中的方式(还有很多,这里以后继续补充)
- uva-10177
- Head First设计模式之开放-关闭原则