K短路(A*算法)

K短路:

在图论中,K短路问题是指在一个图中找到从起点s到终点t的第K短的路径。其中,第1短路径即为最短路径。K短路算法在实际应用中有着广泛的用途,如在通信网络中找到替代的最短路径等。

基本概念
  • K短路:从起点s到终点t的第K短的路径。当K=1时,即为最短路径。
  • Dijkstra算法:一种用于求解单源最短路径的贪心算法,但只能找到一条最短路径。
  • A*算法:一种启发式搜索算法,通过估价函数f(n) = g(n) + h(n)来估计从起点到终点的路径长度,其中g(n)是从起点到当前节点的实际代价,h(n)是从当前节点到终点的最佳路径估计代价。
K短路算法的实现
1. 基于Dijkstra和A*的K短路算法

K短路算法的基本思想是在每次迭代中,找到从起点到终点的第K短路径。通常,我们结合Dijkstra算法和A*算法来实现这一目标。

步骤

  1. 预处理:使用Dijkstra算法从终点t反向计算图中每个节点到t的最短路径长度,存储在数组dis[]中。这个步骤是为了计算A*算法中的h(n)。
  2. 初始化:使用优先队列(最小堆)来存储待扩展的路径,队列中的元素包括当前路径的估价f(n)、实际代价g(n)和当前节点v。
  3. 迭代过程
    • 从队列中取出估价最小的节点now。
    • 如果now是终点t,并且这是第k次到达t,则返回当前路径的总代价。
    • 如果now到达t的次数超过k,则跳过此节点。
    • 否则,扩展now节点,生成所有可能的下一节点,并计算其估价f(next) = g(now) + w(now, next) + dis[next],其中w(now, next)是now到next的边的权重。
    • 将新的节点加入队列中。
  4. 结束条件:如果队列为空,表示没有找到第k条路径,返回-1或其他错误标识。

在这里插入图片描述

推荐相关视频学习链接:【B27 A*算法 第K短路】https://www.bilibili.com/video/BV19k4y1K7gV?vd_source=4c9eb38d8205116069b961c84f64c958

在这里插入图片描述

接下来看一下相关代码(此代码维护二元组)

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
typedef pair<int,int> pii;
const int N = 1e3 + 10, M = 1e5 + 10;

int n, m, S, T, K;
int dist[N], st[N], cnt[N];
vector<pii> e[N], ne[N]; //e数组是用来存从终点T到其余各点的边,实际上是没有这些边的,只是将原来的单向边进行反向,就可以表示出从T到其余各点的边
struct node{
    int s, v, d;
    bool operator<(const node &x) const{
        return s > x.s;//如果你使用的是默认的比较函数std::less<T>(或者没有显式指定比较函数,因为std::less<T>是默认的),那么它会调用元素的operator<(如果已定义)。在这种情况下,operator<的bool返回值表示第一个元素是否“小于”第二个元素,而std::priority_queue会将这些“较小”的元素放在堆的底部(尽管它们仍然很容易被访问,但不是最先被移除的)。然而,由于std::priority_queue是一个最大堆,所以实际上被视为“最大”的元素(即那些operator<返回false的元素)会被放置在堆顶。
    }
};
void dijkstra() {
    memset(dist, 0x3f, sizeof dist);
    dist[T] = 0;//这里求的是从T为起点的,到其余各点的最短距离
    priority_queue<pii, vector<pii>, greater<pii>> q;
    q.push({0, T});
    while(q.size()) {
        int ver = q.top().second, dis = q.top().first;
        q.pop();
        if(st[ver]) continue;
        st[ver] = 1;
        for(pii t : e[ver]) {
            int u = t.first, w = t.second;
            if(!st[u] && dist[ver] + w < dist[u]) {
                dist[u] = dist[ver] + w;
                q.push({dist[u], u});
            }
        }
    }
}
int aStar() {
    priority_queue<node> q; 
    q.push({dist[S], S, 0});
    while(q.size()) {
        int ver = q.top().v, dis = q.top().d;
        q.pop();
        cnt[ver]++;
        if(cnt[T] == K) return dis;
        for(pii t : ne[ver]) {
            int u = t.first, w = t.second;
            q.push({dist[u] + dis + w, u, dis + w});
        }
    } 
    return 0;
}   
int main() {
    cin >> n >> m;
    for(int i = 1; i <= m; i++) {
        int a, b, c; cin >> a >> b >> c;
        ne[a].push_back({b, c});//这里建的是单向边
        e[b].push_back({a, c});//反向建边,和原来边一样,同样是单向边,只不过反向
    }
    cin >> S >> T >> K;
    if(S == T) K++; //这种情况对应有环,即可以自己到自己,第一次从自己出发,所以k++,去掉第一次
    dijkstra();//先预处理从T到其余各点的最短距离,即预测数组
    // for(int i = 1; i <= n; i++) {
    //     cout << "i: " << i << " dist[i]: " << dist[i] << '\n';
    // }
    cout << aStar();

    return 0;
}

在这里插入图片描述

在这里插入图片描述

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

幻听嵩的留香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值