【Bellman-Ford算法(求解最短路径约束问题)】787. K 站中转内最便宜的航班——算法分析

【Bellman-Ford算法(求解最短路径约束问题)】787. K 站中转内最便宜的航班——算法分析

787. K 站中转内最便宜的航班

有 n 个城市通过一些航班连接。给你一个数组 flights ,其中 flights[i] = [fromi, toi, pricei]
,表示该航班都从城市 fromi 开始,以价格 pricei 抵达 toi。

现在给定所有的城市和航班,以及出发城市 src 和目的地 dst,你的任务是找到出一条最多经过 k 站中转的路线,使得从 src 到 dst
的 价格最便宜 ,并返回该价格。 如果不存在这样的路线,则输出 -1。

示例 1:

输入: n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]] src = 0, dst = 2,
k = 1 输出: 200 解释: 城市航班图如下
在这里插入图片描述
从城市 0 到城市 2 在 1 站中转以内的最便宜价格是 200,如图中红色所示。

在学习的最短路径算法中,除了迪杰斯特拉算法、弗洛伊德算法之外,还有其他的算法,Bellman-Ford算法就是其中的一种,该算法解决了迪杰斯特拉算法中无法求解负权图的问题,同时对于最短路径中的约束问题也能够求解。

该算法实质上是动态规划算法,具体的算法过程如下:

给定图G(V, E)(其中V、E分别为图G的顶点集与边集),源点s,数组Distant[i]记录从源点s到顶点i的路径长度,初始化数组Distant[n]为无穷大,Distant[s]为0;

以下操作循环执行至多n-1次,n为顶点数: 对于每一条边e(u, v),如果Distant[u] + w(u, v) <Distant[v],则另Distant[v] = Distant[u]+w(u, v)。w(u, v)为边e(u,v)的权值;
若上述操作没有对Distant进行更新,说明最短路径已经查找完毕,或者部分点不可达,跳出循环。否则执行下次循环;

为了检测图中是否存在负环路,即权值之和小于0的环路。对于每一条边e(u, v),如果存在Distant[u] + w(u, v) <Distant[v]的边,则图中存在负环路,即是说改图无法求出单源最短路径。否则数组Distant[n]中记录的就是源点s到各顶点的最短路径长度。

可知,Bellman-Ford算法寻找单源最短路径的时间复杂度为O(V*E).

对于这道题目,我们可以使用该算法的思想:

我们用 f [ t ] [ i ] f[t][i] f[t][i] 表示通过恰好 tt 次航班,从出发城市 s r c src src 到达城市 i i i 需要的最小花费。在进行状态转移时,我们可以枚举最后一次航班的起点 j j j,即:

f[t][i]= (j,i)∈flightsmin{f[t−1][j]+cost(j,i)}
其中 ( j , i ) ∈ f l i g h t s (j, i)∈flights (j,i)flights 表示在给定的航班数组 f l i g h t s flights flights 中存在从城市 j j j 出发到达城市 i i i 的航班, c o s t ( j , i ) cost(j,i) cost(j,i) 表示该航班的花费。该状态转移方程的意义在于,枚举最后一次航班的起点 j j j,那么前 t − 1 t-1 t1 次航班的最小花费为 f [ t − 1 ] [ j ] f[t−1][j] f[t1][j] 加上最后一次航班的花费 c o s t ( j , i ) cost(j,i) cost(j,i) 中的最小值,即为 f [ t ] [ i ] f[t][i] f[t][i]

由于我们最多只能中转 k k k 次,也就是最多搭乘 k + 1 k+1 k+1 次航班,最终的答案即为 f [ 1 ] [ d s t ] , f [ 2 ] [ d s t ] , ⋯ , f [ k + 1 ] [ d s t ] f[1][dst],f[2][dst],⋯,f[k+1][dst] f[1][dst],f[2][dst],,f[k+1][dst]中的最小值。

【代码如下】

class Solution {
public:
    int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int k) {
        //  f[t][j] 表示经过t个中转(t+1条边)后到达j所需要的最小花费
        vector<vector<int>> f(k+2,vector<int>(n,10000 * 101 + 1));
        //  转移方程:f[t][j] = min(f[t-1][i]+cost(i,j)) 
        f[0][src] = 0;
        for(int t = 1;t<=k+1;t++){
            for(auto&& flight:flights){
                int i = flight[0];
                int j = flight[1];
                int cost = flight[2];
                f[t][j] = min(f[t][j],f[t-1][i] + cost);
            }
        }
        //  结果t = 0-k的f[t][j]最小值
        int ans = 10000 * 101 + 1;
        for(int t = 0;t<=k+1;t++){
            ans = min(ans, f[t][dst]);
        }
        return ans == 10000 * 101 + 1?-1:ans;
    }
};

参考文献:
Bellman-Ford算法详解
Bellman-Ford算法
K 站中转内最便宜的航班

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Yozu_Roo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值