POJ 2449 Remmarguts' Date [A*算法 K短路]
来源:互联网 发布:淘宝swot分析 编辑:程序博客网 时间:2024/06/06 02:39
题目链接: POJ 2449 Remmarguts’ Date
题目大意:
给一个图,求第k短的路。
输入格式:
第一行两个数 N(1<= N <= 1000), M(0 <= M <= 100000)。其中N表示顶点数, M表示边数。
接下来M行,每行三个数,a, b, t, 表示从a到b的边,权为k。
最后一行三个数, S, T, K, 表示让你求从S到T的第K短路径的长度
输出格式:
一个数, 第K短路径的长度
样例输入:
2 2
1 2 5
2 1 4
1 2 2
样例输出:
14
题解:
借着这题学一下A*算法。之前做过一题,求次短路(POJ 3255-Roadblocks [次短路 Dijkstra]),当时得知了A*可以求解K短路,不过没有深究。现在再返回来研究一下K短路的问题。
看了好多别人的总结,找到一系列非常棒的文章《关于寻路算法的一些思考》这是我看到的讲的最清晰的文章了。
这里还有一个A*算法可视化的Demo,也是在上面的文章里找到的。
先来看下Wikipedia对A*算法的定义:
A*算法
A*搜索算法,俗称A星算法。这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法。常用于游戏中的NPC的移动计算,或在线游戏的BOT的移动计算上。
该算法综合了Best-First Search和Dijkstra算法的优点:在进行启发式搜索提高算法效率的同时,可以保证找到一条最优路径(基于评估函数)。
在此算法中,如果以 g(n)表示从起点到任意顶点n的实际距离,h(n)表示任意顶点n到目标顶点的估算距离(根据所采用的评估函数的不同而变化),那么 A*算法的估算函数为:
f(n)=g(n)+h(n)
这个公式遵循以下特性:
如果g(n)为0,即只计算任意顶点n到目标的评估函数h(n),而不计算起点到顶点n的距离,则算法转化为使用贪心策略的Best-First Search,速度最快,但可能得不出最优解;
如果h(n)不为0,则一定可以求出最优解,而且h(n)越小,需要计算的节点越多,算法效率越低,常见的评估函数有——欧几里得距离、曼哈顿距离、切比雪夫距离;
如果h(n)为0,即只需求出起点到任意顶点n的最短路径g(n),而不计算任何评估函数h(n),则转化为单源最短路径问题,即Dijkstra算法,此时需要计算最多的定点;
定义看的不是很理解,可以看下《关于寻路算法的一些思考》,比很多讲得一知半解的博客强得多。
伪代码:
function A*(start,goal) closedset := the empty set //已经被估算的节点集合 openset := set containing the initial node //将要被估算的节点集合 came_from := empty map g_score[start] := 0 //g(n) h_score[start] := heuristic_estimate_of_distance(start, goal) //h(n) f_score[start] := h_score[start] //f(n)=h(n)+g(n),由于g(n)=0,所以…… while openset is not empty //当将被估算的节点存在时,执行 x := the node in openset having the lowest f_score[] value //取x为将被估算的节点中f(x)最小的 if x = goal //若x为终点,执行 return reconstruct_path(came_from,goal) //返回到x的最佳路径 remove x from openset //将x节点从将被估算的节点中删除 add x to closedset //将x节点插入已经被估算的节点 foreach y in neighbor_nodes(x) //对于节点x附近的任意节点y,执行 if y in closedset //若y已被估值,跳过 continue tentative_g_score := g_score[x] + dist_between(x,y) //从起点到节点y的距离 if y not in openset //若y不是将被估算的节点 add y to openset //将y插入将被估算的节点中 tentative_is_better := true elseif tentative_g_score < g_score[y] //如果y的估值小于y的实际距离 tentative_is_better := true //暂时判断为更好 else tentative_is_better := false //否则判断为更差 if tentative_is_better = true //如果判断为更好 came_from[y] := x //将y设为x的子节点 g_score[y] := tentative_g_score h_score[y] := heuristic_estimate_of_distance(y, goal) f_score[y] := g_score[y] + h_score[y] return failure function reconstruct_path(came_from,current_node) if came_from[current_node] is set p = reconstruct_path(came_from,came_from[current_node]) return (p + current_node) else return current_node
K短路
A*算法可以找到从原点S到终点T的一条最短路,然后结束算法。但A*算法同样也可以用来寻找K短路。
如果我们不设置closeset,A*算法的搜索空间里会有很多冗余的重复状态,但我们每次都只搜索距离终点最近的节点,所以如果终点可达,最终我们第一次搜索到终点T时,一定就是找到了一条最短路。当我们下一次又搜索到终点T时,一定有是当前的最优的路径,所以就是次短路……这样我们就可以得到我们要求的K短路了。
这题的难点也就是评估函数h(n)的设置。当评估函数h(n)恰好等于实际距离h*(n)时,A*算法是非常高效的。我们可以在给定图G的反向图G’中从T点执行一次Dijkstra,求得G’中T到所有其他节点的最短距离,就是图G中所有其他节点到T的最短距离,这恰好就是我们需要的h*(n)。
g(n)是从S到点n的最短距离,可以在A*算法的执行过程中逐步求得。
然后,我们就可以使用f(n)=g(n)+h(n) 来给openset设置优先级,每次取出权最小的节点进行搜索,直到搜索到T点K次,算法结束。
如果T点是从S点不可达的,则不加限制的话算法将永远无法结束。我们需要找K短路,那么每个节点都不可能出现大于K次。所以当某个节点出现了大于K次时,就不要再该节点上继续扩展搜索空间了。
代码:
#include <iostream>#include <algorithm>#include <vector>#include <cstring>#include <queue>#define MAXN 10000#define INF 0x3f3f3f3fusing namespace std;typedef pair<int, int> P;int N, M, S, T, K;int dist[MAXN];int tdist[MAXN];int cnt[MAXN];bool f[MAXN];vector<P> Adj[MAXN];vector<P> Rev[MAXN];struct Edge { int to, len; Edge(){} Edge(int t, int l):to(t), len(l){}};priority_queue<Edge> q;bool operator<(const Edge &a, const Edge &b) { return (a.len + dist[a.to]) > (b.len + dist[b.to]);}void dijkstra() { memset(dist, 0, sizeof(dist)); fill(tdist, tdist+MAXN, INF); tdist[T] = 0; while(!q.empty()) q.pop(); q.push(Edge(T, 0)); while (!q.empty()) { int x = q.top().to; int d = q.top().len; q.pop(); if (tdist[x] < d) continue; for (int i = 0; i < Rev[x].size(); i++) { int y = Rev[x][i].first; int len = Rev[x][i].second; if (d+ len < tdist[y]) { tdist[y] = d + len; q.push(Edge(y, tdist[y])); } } } for (int i = 1; i <= N; i++){ dist[i] = tdist[i]; }}int aStar() { if (dist[S] == INF) return -1; while (!q.empty()) q.pop(); q.push(Edge(S, 0)); memset(cnt, 0, sizeof(cnt)); while (!q.empty()) { int x = q.top().to; int d = q.top().len; q.pop(); cnt[x]++; if (cnt[T] == K) return d; if (cnt[x] > K) continue; for (int i = 0; i < Adj[x].size(); i++) { int y = Adj[x][i].first; int len = Adj[x][i].second; q.push(Edge(y, d+len)); } } return -1;}int main() { std::ios::sync_with_stdio(false); int a, b, t; cin >> N >> M; for (int i = 0; i < M; i++) { cin >> a >> b >> t; Adj[a].push_back(make_pair(b, t)); Rev[b].push_back(make_pair(a, t)); } cin >> S >> T >> K; //本题需要特别注意的地方,S==K时,也得走出去转一圈。。。- -! if (S == T) K++; dijkstra(); cout << aStar() << endl; return 0;}
- poj 2449 Remmarguts' Date(K短路,A*算法)
- POJ 2449 Remmarguts' Date [A*算法 K短路]
- POJ 2449 Remmarguts' Date (A*+K短路)
- 【K短路】【A星】Remmarguts' Date POJ 2449 A-Star
- POJ 2449 Remmarguts' Date 第K短路 A* + SPFA
- poj 2449 Remmarguts' Date--k短路--spfa+A*
- poj 2449 Remmarguts' Date(K短路 Spfa+A*)
- poj 2449 Remmarguts' Date (K短路+A*+Dijkstra)
- poj 2449 Remmarguts' Date(第K短路 A*)
- POJ 2449Remmarguts' Date (A* 求K短路)
- poj 2449 Remmarguts' Date(A*求第K短路)
- poj-2449-Remmarguts' Date-A*+求K短路
- POJ 2449 Remmarguts' Date(A*+SPFA)K短路问题
- poj 2449 Remmarguts’ Date第k短路 dij+A*
- POJ--2449--Remmarguts' Date【dijkstra_heap+A*】第K短路
- POJ 2449 Remmarguts' Date ( Dijkstra + A* 求解第K短路 )
- POJ 2449 Remmarguts' Date 第K短路 A*
- POJ 2449 Remmarguts' Date(A* - 第K短路)
- 1. Two Sum
- 欢迎使用CSDN-markdown编辑器
- vb.net 教程 5-14 图像处理之内存处理基础2
- 面向对象的四个基本特征
- The Triangle
- POJ 2449 Remmarguts' Date [A*算法 K短路]
- Java面试题全集(中)
- 计算机图形学——OpenGL之光照
- Fragment和ViewPager嵌套使用
- 做题技巧
- 进程和线程的区别
- 关闭MTK电池检测
- 王道机试第三章总结
- POJ-3661 Running(dp)