HDU1043_Eight_A*算法&康托展开
来源:互联网 发布:小班防火知多少课件 编辑:程序博客网 时间:2024/05/16 08:07
题意
八数码问题,不必赘述。
思路
BFS 在 POJ 过,然而 HDU T成狗。
正解为 A* ,加上 康托展开 压缩状态。
A*算法
A* 算法的核心是公式 f = g + h。其中,g 起始状态到当前状态的距离,h 当前状态到目标状态的估计距离。
BFS 是 A* 的一种特殊状况, h 恒等于 0。
h 的估计
如果h(n)< d(n)到目标状态的实际距离,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。
如果h(n)=d(n),即距离估计h(n)等于最短距离,那么搜索将严格沿着最短路径进行, 此时的搜索效率是最高的。
如果 h(n)>d(n),搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。
实现起来与 BFS 很相似,要用到优先队列。
A* 算法的缺点是占用内存随问题的规模指数增长。据说 IDA* 能弥补这一点,以后再学。
可能是因为这个原因,有一个迷之 MLE 的地方。具体见 AC 代码中注释的注意点把。
本题中把状态中每个数字到正确位置的欧氏距离和作为估计值。
康托展开
就是用从小到大的次序序号代表长度为 n 的排列,可以把状态数压缩到 n!。
求每个排列的序号可以求小于它的排列的个数,用数位DP的思想。枚举相等的位,以下的位便不受限制。特别的,第 t 位 (n - 1 - t)! * k ,其中 k 是 t 位之后的小于第 t 为的数字的个数。
题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=1043
AC代码
#include<iostream>#include<cstdio>#include<algorithm>#include<queue>#include<cstdlib>#include<cstring>using namespace std;const int maxn = 4e5 + 10;const int mx[4] = {-1, 0, 1, 0};const int my[4] = {0, 1, 0, -1};const char op[7] = "urdl";struct node //保存 A* 状态的结构体{ int f, g, h; //f = g + h int has; //康托展开后的哈希值 int m[3][3]; //保存当前状态图 int x, y; //空白格的位置 bool operator < (node b) const {return f == b.f ? g > b.g : f > b.f;} //重载 < 号,优先队列};bool vis[maxn]; //A* 的访问标记int pre[maxn]; //保存路径中的前缀char chr[maxn]; //保存路径中的操作int get_h(int m[][3]) //状态中每个数字到正确位置的欧氏距离和作为估计值{ int res = 0; for(int i= 0; i< 3; i++) for(int j= 0; j< 3; j ++) res += abs((m[i][j] - 1) / 3 - i) + abs((m[i][j] - 1) % 3 - j); return res;}const int can[9] = {1,1,2,6,24,120,720,5040,40320}; //9个数的康托展开int Cantor(int m[][3]){ int res = 0, k = 0; int temp[10]; //保存到一维数组中便于计算 for(int i= 0; i< 3; i++) for(int j= 0; j< 3; j++) temp[k ++] = m[i][j]; for(int i= 0; i< 9; i++) //类比数位DP的原理 { k = 0; for(int j= i + 1; j< 9; j++) if(temp[j] < temp[i]) k ++; res += can[9 - 1 - i] * k; } return res;}void print(int x) //输出路径{ if(pre[x] == -1) return; print(pre[x]); printf("%c", chr[x]);}void Astar(node s) //A* 搜索{ if(s.has == 0) //起始状态就是目标状态 { printf("\n"); return; } priority_queue<node> qu; //从初始状态出发 memset(vis, false, sizeof vis); vis[s.has] = true; qu.push(s); pre[s.has] = -1; while(qu.size()) { node v = qu.top(); qu.pop(); for(int i= 0; i< 4; i++) { int xx = v.x + mx[i], yy = v.y + my[i]; if(xx < 0 || yy < 0 || xx >= 3 || yy >= 3) continue; node u = v; swap(u.m[u.x][u.y], u.m[xx][yy]); u.has = Cantor(u.m); if(vis[u.has]) continue; //判断当前状态是否访问过 vis[u.has] = true;//注意,在这里做访问标记,不是从队列中取出的时候,借助dijkstra理解 pre[u.has] = v.has, chr[u.has] = op[i]; //记录路径 if(u.has == 0) //到达最终状态,注意点 { print(0); printf("\n"); return; } u.g ++; //填写状态信息 u.h = get_h(u.m); u.f = u.g + u.h; u.x = xx, u.y = yy; qu.push(u); //加入队列 } }}int main(){ char str[30]; node s; while(gets(str)) { for(int i= 0, j = 0; i< 9 && str[j] != '\n'; j++) //建立起点的状态 { if(str[j] == ' ') continue; else if(str[j] == 'x') s.m[i / 3][i % 3] = 9, s.x = i / 3, s.y = i % 3; else s.m[i / 3][i % 3] = str[j] - '0'; i ++; } s.g = 0; s.h = get_h(s.m); s.f = s.g + s.h; s.has = Cantor(s.m); int k = 0; //通过逆序数的奇偶判断是否可解 for(int i= 0; i< 9; i++) for(int j= 0; j< i; j++) if(s.m[j / 3][j % 3] != 9 && s.m[i / 3][i % 3] < s.m[j / 3][j % 3]) k ++; if(k & 1) printf("unsolvable\n"); else Astar(s); //注意点 } return 0;}
阅读全文
0 0
- HDU1043_Eight_A*算法&康托展开
- 【算法】康托展开
- 康托展开/逆康托展开
- 康托展开 & 康托逆展开
- 康托展开&逆康托展开
- 康托展开 康托逆展开
- 康托展开
- 康托展开
- 康托展开
- 康托展开
- 康托展开
- 康托展开
- 康托展开
- 康托展开公式
- 康托展开
- 康托展开
- 康托展开
- 康托展开
- 1.数据库的安装/卸载
- red hat的yum源修改为centos的yum源
- 好几个8
- PHP获取表单里各项值总结
- Keras学习之三:用CNN实现cifar10图像分类模型
- HDU1043_Eight_A*算法&康托展开
- 抽象类和接口
- 将二叉搜索树转换成一个排序的双向链表
- POJ 2676 Sudoku
- 【C语言】结构体
- 最小生成树
- 技术问题问答总结
- 无外网情况下Centos7--为Hadoop集群搭建Sentry(上)
- 基础搜索题 最少转弯问题