八数码难题之 A* 算法
人生第一个A*算法~好激动……
八数码难题……又称八数码水题,首先要理解一些东西:
1.状态可以转化成整数,比如状态: 1 2 3
4 5 6
7 8 0
可以转化成:123456780这个整数
2.看上去有9*9种不同状态,实际上只有9!种(即9*8*7*6*5*4*3*2*1)种状态,哈希表大小开成一个大素数即可,每次用这个大素数去对当前的状态(整数)取余获得哈希表的键值
3.移动一个数码到0这个位置可以理解成0这个数码移动到四周的数码的位置
我先用广搜水过,速度大约是20ms左右:
- 1 #include <iostream>
- 2 #include <queue>
- 3 #include <cstdlib>
- 4 #define hash(x) (x%14233333)
- 5 using namespace std;
- 6 const int _end=123804765;
- 7 int _sta;
- 8 bool h_tab[14233333];
- 9 queue<int> que,tque;
- 10 int move(int key,char c)
- 11 {
- 12 int p0=1,kb=key,_map[3][3],x0,y0;
- 13 while(1){if(kb%10==0)break;p0++,kb/=10;}
- 14 kb=key,x0=(9-p0)/3,y0=(9-p0)%3;
- 15 for(int i=2;i>=0;i--)for(int j=2;j>=0;j--)_map[i][j]=kb%10,kb/=10;
- 16 switch(c)
- 17 {
- 18 case 'l':if(y0==0)return key;swap(_map[x0][y0],_map[x0][y0-1]);break;
- 19 case 'r':if(y0==2)return key;swap(_map[x0][y0],_map[x0][y0+1]);break;
- 20 case 'u':if(x0==0)return key;swap(_map[x0][y0],_map[x0-1][y0]);break;
- 21 case 'd':if(x0==2)return key;swap(_map[x0][y0],_map[x0+1][y0]);break;
- 22 default:break;
- 23 }
- 24 kb=0;
- 25 for(int i=0;i<3;i++)for(int j=0;j<3;j++)kb=kb*10+_map[i][j];
- 26 return kb;
- 27 }
- 28 int main()
- 29 {
- 30 cin>>_sta;
- 31 if(_sta==_end)cout<<0,exit(0);
- 32 que.push(_sta),tque.push(0),h_tab[hash(_sta)]=1;
- 33 while(!que.empty())
- 34 {
- 35 int _new=move(que.front(),'u');
- 36 if(_new==_end)cout<<tque.front()+1,exit(0);
- 37 if(!h_tab[hash(_new)])h_tab[hash(_new)]=1,que.push(_new),tque.push(tque.front()+1);
- 38 _new=move(que.front(),'d');
- 39 if(_new==_end)cout<<tque.front()+1,exit(0);
- 40 if(!h_tab[hash(_new)])h_tab[hash(_new)]=1,que.push(_new),tque.push(tque.front()+1);
- 41 _new=move(que.front(),'l');
- 42 if(_new==_end)cout<<tque.front()+1,exit(0);
- 43 if(!h_tab[hash(_new)])h_tab[hash(_new)]=1,que.push(_new),tque.push(tque.front()+1);
- 44 _new=move(que.front(),'r');
- 45 if(_new==_end)cout<<tque.front()+1,exit(0);
- 46 if(!h_tab[hash(_new)])h_tab[hash(_new)]=1,que.push(_new),tque.push(tque.front()+1);
- 47 que.pop(),tque.pop();
- 48 }
- 49 return 0;
- 50 }
又用了双向广搜试了试,速度大概是10ms左右:
- 1 #include <iostream>
- 2 #include <queue>
- 3 #include <cstdlib>
- 4 #define hash(x) (x%14233333)
- 5 using namespace std;
- 6 const int _end=123804765;
- 7 int _sta;
- 8 unsigned short th_tab[2][14233333];
- 9 bool h_tab[2][14233333];
- 10 queue<int> que[2];
- 11 int move(int key,char c)
- 12 {
- 13 int p0=1,kb=key,_map[3][3],x0,y0;
- 14 while(1){if(kb%10==0)break;p0++,kb/=10;}
- 15 kb=key,x0=(9-p0)/3,y0=(9-p0)%3;
- 16 for(int i=2;i>=0;i--)for(int j=2;j>=0;j--)_map[i][j]=kb%10,kb/=10;
- 17 switch(c)
- 18 {
- 19 case 'l':if(y0==0)return key;swap(_map[x0][y0],_map[x0][y0-1]);break;
- 20 case 'r':if(y0==2)return key;swap(_map[x0][y0],_map[x0][y0+1]);break;
- 21 case 'u':if(x0==0)return key;swap(_map[x0][y0],_map[x0-1][y0]);break;
- 22 case 'd':if(x0==2)return key;swap(_map[x0][y0],_map[x0+1][y0]);break;
- 23 default:break;
- 24 }
- 25 kb=0;
- 26 for(int i=0;i<3;i++)for(int j=0;j<3;j++)kb=kb*10+_map[i][j];
- 27 return kb;
- 28 }
- 29 int main()
- 30 {
- 31 cin>>_sta;
- 32 if(_sta==_end)cout<<0,exit(0);
- 33 que[0].push(_sta),que[1].push(_end),h_tab[0][hash(_sta)]=h_tab[1][hash(_end)]=1,th_tab[0][hash(_sta)]=th_tab[1][hash(_end)]=0;
- 34 while(!que[0].empty()||!que[1].empty())
- 35 {
- 36 int _new;
- 37 if(!que[0].empty())
- 38 {
- 39 _new=move(que[0].front(),'u');
- 40 if(!h_tab[0][hash(_new)])
- 41 {
- 42 h_tab[0][hash(_new)]=1,que[0].push(_new),th_tab[0][hash(_new)]=th_tab[0][hash(que[0].front())]+1;
- 43 if(h_tab[1][hash(_new)])cout<<th_tab[0][hash(_new)]+th_tab[1][hash(_new)],exit(0);
- 44 }
- 45 _new=move(que[0].front(),'d');
- 46 if(!h_tab[0][hash(_new)])
- 47 {
- 48 h_tab[0][hash(_new)]=1,que[0].push(_new),th_tab[0][hash(_new)]=th_tab[0][hash(que[0].front())]+1;
- 49 if(h_tab[1][hash(_new)])cout<<th_tab[0][hash(_new)]+th_tab[1][hash(_new)],exit(0);
- 50 }
- 51 _new=move(que[0].front(),'l');
- 52 if(!h_tab[0][hash(_new)])
- 53 {
- 54 h_tab[0][hash(_new)]=1,que[0].push(_new),th_tab[0][hash(_new)]=th_tab[0][hash(que[0].front())]+1;
- 55 if(h_tab[1][hash(_new)])cout<<th_tab[0][hash(_new)]+th_tab[1][hash(_new)],exit(0);
- 56 }
- 57 _new=move(que[0].front(),'r');
- 58 if(!h_tab[0][hash(_new)])
- 59 {
- 60 h_tab[0][hash(_new)]=1,que[0].push(_new),th_tab[0][hash(_new)]=th_tab[0][hash(que[0].front())]+1;
- 61 if(h_tab[1][hash(_new)])cout<<th_tab[0][hash(_new)]+th_tab[1][hash(_new)],exit(0);
- 62 }
- 63 que[0].pop();
- 64 }
- 65 if(!que[1].empty())
- 66 {
- 67 _new=move(que[1].front(),'u');
- 68 if(!h_tab[1][hash(_new)])
- 69 {
- 70 h_tab[1][hash(_new)]=1,que[1].push(_new),th_tab[1][hash(_new)]=th_tab[1][hash(que[1].front())]+1;
- 71 if(h_tab[0][hash(_new)])cout<<th_tab[1][hash(_new)]+th_tab[0][hash(_new)],exit(0);
- 72 }
- 73 _new=move(que[1].front(),'d');
- 74 if(!h_tab[1][hash(_new)])
- 75 {
- 76 h_tab[1][hash(_new)]=1,que[1].push(_new),th_tab[1][hash(_new)]=th_tab[1][hash(que[1].front())]+1;
- 77 if(h_tab[0][hash(_new)])cout<<th_tab[1][hash(_new)]+th_tab[0][hash(_new)],exit(0);
- 78 }
- 79 _new=move(que[1].front(),'l');
- 80 if(!h_tab[1][hash(_new)])
- 81 {
- 82 h_tab[1][hash(_new)]=1,que[1].push(_new),th_tab[1][hash(_new)]=th_tab[1][hash(que[1].front())]+1;
- 83 if(h_tab[0][hash(_new)])cout<<th_tab[1][hash(_new)]+th_tab[0][hash(_new)],exit(0);
- 84 }
- 85 _new=move(que[1].front(),'r');
- 86 if(!h_tab[1][hash(_new)])
- 87 {
- 88 h_tab[1][hash(_new)]=1,que[1].push(_new),th_tab[1][hash(_new)]=th_tab[1][hash(que[1].front())]+1;
- 89 if(h_tab[0][hash(_new)])cout<<th_tab[1][hash(_new)]+th_tab[0][hash(_new)],exit(0);
- 90 }
- 91 que[1].pop();
- 92 }
- 93 }
- 94 return 0;
- 95 }
然后我一条路走到黑,研究起了A*算法……
A*算法我现在研究也不透彻……也是一种广搜,它通过一个估值函数,估算当前扩展出的新状态到目标状态的代价,再选中代价最小的新状态扩展,直到扩展到目标状态……
非常快,速度大概是0~1ms
先转发一个写A*算法很不错的博客:http://www.cnblogs.com/yanlingyin/archive/2012/01/15/2322640.html
再贴个代码:
- 1 #include <iostream>
- 2 #include <deque>
- 3 #include <algorithm>
- 4 #include <cstdlib>
- 5 #define _abs(x) (x>=0?x:-x)//绝对值函数
- 6 #define h_size 14233333//哈希表大小
- 7 #define hash(x) (x%h_size)
- 8 using namespace std;
- 9 typedef pair<int,int> pii;
- 10 const int _end=123804765,_enp[2][9]={{1,0,0,0,1,2,2,2,1},{1,0,1,2,2,2,1,0,0}};//end为目标状态,enp[0][i]表示目标状态中数字i所在的行,enp[1][i]表示数字i所在的列
- 11 int _sta;//开始状态
- 12 bool _clo[h_size];//a*算法的close表
- 13 deque<pii> _ol;//a*算法的open表(使用双端队列)
- 14 int h(int key)//估值函数,使用曼哈顿距离估值
- 15 {
- 16 int _map[3][3],_kp[2][9],sum=0;
- 17 for(int i=2;i>=0;i--)for(int j=2;j>=0;j--)_kp[0][key%10]=i,_kp[1][key%10]=j,key/=10;
- 18 for(int i=0;i<9;i++)sum+=abs(_kp[0][i]-_enp[0][i])+abs(_kp[1][i]-_enp[1][i]);
- 19 return sum;
- 20 }
- 21 int move(int key,char _ctr)//移动数字0函数,u表示向上,d表示向下,l表示向左,r表示向右
- 22 {
- 23 int _kb=key,_map[3][3],i0,j0;
- 24 for(int i=2;i>=0;i--)for(int j=2;j>=0;j--){_map[i][j]=_kb%10,_kb/=10;if(_map[i][j]==0)i0=i,j0=j;}
- 25 switch(_ctr)//如果无法扩展(即到达边界)就返回移动前的值
- 26 {
- 27 case 'u':if(i0==0)return key;swap(_map[i0][j0],_map[i0-1][j0]);break;
- 28 case 'd':if(i0==2)return key;swap(_map[i0][j0],_map[i0+1][j0]);break;
- 29 case 'l':if(j0==0)return key;swap(_map[i0][j0],_map[i0][j0-1]);break;
- 30 case 'r':if(j0==2)return key;swap(_map[i0][j0],_map[i0][j0+1]);break;
- 31 }
- 32 for(int i=0;i<3;i++)for(int j=0;j<3;j++)_kb=_kb*10+_map[i][j];
- 33 return _kb;
- 34 }
- 35 bool cmp(pii a,pii b){return a.second<b.second;}//二分查找比较函数
- 36 void work(pii _nex)//处理新状态
- 37 {
- 38 if(_nex.first==_end)cout<<_nex.second-h(_nex.first),exit(0);//发现正解,直接输出并结束程序
- 39 if(!_clo[hash(_nex.first)])_ol.insert(lower_bound(_ol.begin(),_ol.end(),_nex,cmp),_nex);//二分查找插入新状态
- 40 }
- 41 int main()
- 42 {
- 43 cin>>_sta;
- 44 _ol.push_back(make_pair(_sta,h(_sta)));//把开始状态加入到open表中
- 45 while(!_ol.empty())//处理open表
- 46 {
- 47 pii _now=_ol.front();
- 48 _ol.pop_front(),_clo[hash(_now.first)]=1;//把当前open表中的最优状态取出并加入到close表中
- 49 int _nex=move(_now.first,'u');//扩展新状态
- 50 work(make_pair(_nex,_now.second-h(_now.first)+h(_nex)+1)),_nex=move(_now.first,'d');
- 51 work(make_pair(_nex,_now.second-h(_now.first)+h(_nex)+1)),_nex=move(_now.first,'l');
- 52 work(make_pair(_nex,_now.second-h(_now.first)+h(_nex)+1)),_nex=move(_now.first,'r');
- 53 work(make_pair(_nex,_now.second-h(_now.first)+h(_nex)+1));
- 54 }
- 55 return 0;
- 56 }
八数码难题之 A* 算法的更多相关文章
- luogu P1379 八数码难题(A*算法入门详细讲解)
代码实现细节 #include<cstdio> #include<cstring> #include<iostream> using namespace std; ...
- 双向广搜+hash+康托展开 codevs 1225 八数码难题
codevs 1225 八数码难题 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description Yours和zero在研究A*启 ...
- Codevs 1225 八数码难题
1225 八数码难题 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description Yours和zero在研究A*启发式算法.拿到一道经典的 ...
- [luogu]P1379 八数码难题[广度优先搜索]
八数码难题 ——!x^n+y^n=z^n 我在此只说明此题的一种用BFS的方法,因为本人也是初学,勉勉强强写了一个单向的BFS,据说最快的是IDA*(然而蒟蒻我不会…) 各位如果想用IDA*的可以看看 ...
- 洛谷P1379八数码难题
题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中. 要求解的问题是:给出一种初始布局(初始状态)和目标布局(为 ...
- 洛谷 P1379 八数码难题 解题报告
P1379 八数码难题 题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初 ...
- 【洛谷P1379】八数码难题(广搜、A*)
八数码难题 题目描述 一.广搜: 首先要考虑用什么存每一个状态 显然每个状态都用一个矩阵存是很麻烦的. 我们可以考虑将一个3*3的矩阵用一个字符串或long long 存. 每次扩展时再转化为矩阵. ...
- 习题:八数码难题(双向BFS)
八数码难题(wikioi1225) [题目描述] 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出 ...
- 「LuoguP1379」 八数码难题(迭代加深
[P1379]八数码难题 - 洛谷 题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种 ...
随机推荐
- Golang控制子gorutine退出,并阻塞等待所有子gorutine全部退出
Golang控制子gorutine退出,并阻塞等待所有子gorutine全部退出 需求 程序有时需要自动重启或者重新初始化一些功能,就需要退出之前的所有子gorutine,并且要等待所有子goruti ...
- Pytest学习笔记6-自定义标记mark
前言 在pytest中,我们可以使用mark进行用例的自定义标记,通过不同的标记实现不同的运行策略 比如我们可以标记哪些用例是生产环境执行的,哪些用例是测试环境执行的,在运行代码的时候指定对应的mar ...
- 10、Jenkins配置
10.0.服务器说明: 服务器名称 ip地址 slave-node1 172.16.1.91 10.1.持续集成: 1.什么是持续集成: 持续集成是一种软件开发时实践,即团队开发成员经常集成他们的工作 ...
- Gym 100169E Tetrahedron Inequality
大致题意: 给出六条边,判断是否能组成四面体 分析: 四面体由四个三角形组成,所以每一条边肯定要符合三角形的任意两边大于第三边的性质.一开始以为这样判断就可以了,然而这题并没有这么简单. 如右图,有四 ...
- PostgreSQL角色问题
角色 PostgreSQL使用角色的概念管理数据库访问权限. 根据角色自身的设置不同,一个角色可以看做是一个数据库用户,或者一组数据库用户. 角色可以拥有数据库对象(比如表)以及可以把这些对象上的权限 ...
- AcWing 239. 奇偶游戏
小A和小B在玩一个游戏. 首先,小A写了一个由0和1组成的序列S,长度为N. 然后,小B向小A提出了M个问题. 在每个问题中,小B指定两个数 l 和 r,小A回答 S[l~r] 中有奇数个1还是偶数个 ...
- Docker中容器的备份和恢复(可迁移)
官方文档 备份容器 - save 查看镜像$ docker images 容器快照 - commit$ docker commit CONTAINER xxx/exampleimage-backup: ...
- linux sort uniq命令详解
sort 功能说明:将文本文件内容加以排序,sort可针对文本文件的内容,以行为单位来排序. sort [-bcdfimMnr][-o<输出文件>][-t<分隔字符>][+&l ...
- ADC电阻分压采集相关知识
1.电池串联可以增加电压,电池并联可以增加电流,都是为了增加电功率.但是电池不宜并联,即使是相同规格的电池,如果消耗不同,电势就会不同,会造成电池之间的放电现象. 2.电阻的作用: 限流:为使通过用电 ...
- ARTS起始篇
ARTS简要说明(每周需要完成以下四项): Algorithm:每周至少做一道 leetcode 的算法题,编程训练.刻意练习. Review:需要阅读并点评至少一篇英文技术文章,这个是四项里面对我最 ...