43、K 站中转内最便宜的航班(即求最短路径)

这篇博客介绍了如何运用迪杰斯特拉算法解决寻找从src到dst城市,最多经过k站中转的最便宜航班路径问题。通过构建图并使用优先队列实现Dijkstra算法的变种,当找到目标城市或中转次数耗尽时返回最便宜价格。同时,文章对比了最短路径算法中的迪杰斯特拉和弗洛伊德算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 一、问题

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

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

 

 二、思路

方法一:迪杰斯特拉算法

与 Dijkstra 算法的不同:限制了可以中转 K 次。我们来看下面的例子。

image.png

 

用一个和dist等长的数组stops去记录这个最优价格dist[i]所对应的剩余中转站个数。如果weight_i + dist >= dist[i],我们要看一下当前剩余的中转站次数是否大于stops[i],如果是的话我们依然要将节点i入栈。这样如果最优距离对应的路是一条死胡同,我们也可以检验现在的路。

class Solution {
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
        // Build the graph
        int mat[][] = new int[n][n];
        for (int flight[] : flights) {
            mat[flight[0]][flight[1]] = flight[2];
        }

        // min heap: {(vertex, cost, stops), ...}
        PriorityQueue<int[]> minHeap = new PriorityQueue<>((e1, e2) -> e1[1] - e2[1]);

        // costs[i]: min cost from src to vertex i
        // stops[i]: number of stops of the corresponding cheapest cost for vertex i
        int costs[] = new int[n];
        int stops[] = new int[n];
        Arrays.fill(costs, Integer.MAX_VALUE);

        // Dijkstra Algorithm within k
        minHeap.offer(new int[] {src, 0, k});
        while (!minHeap.isEmpty()) {
            int elem[] = minHeap.poll();
            int vertex = elem[0], cost = elem[1], stop = elem[2];

            if (vertex == dst) {
                return cost;
            } else if (stop < 0) {
                continue;
            }

            for (int i = 0; i < n; ++i) {
                if (mat[vertex][i] > 0) {
                    int costI = costs[i], costVI = mat[vertex][i];
                    if (cost + costVI < costI) {
                        minHeap.offer(new int[] {i, costVI + cost, stop - 1});
                        costs[i] = costVI + cost;
                        stops[i] = stop - 1;
                    } else if (stops[i] < stop - 1) {
                        minHeap.offer(new int[] {i, costVI + cost, stop - 1});
                    }
                }
            }
        }
        return -1;
    }
}

 

最短路径,是指两顶点之间经过的边上权值之和最少的路径。并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。

关于最短路径主要有两种算法:迪杰斯特拉(Dijkstra) 算法弗洛伊德(Floyd) 算法

 

三、迪杰斯特拉算法

计算的是 从某个源点到其余各顶点的最短路径。具体步骤如下:

  1. 先将源点v0加入顶点集S,计算v0到其他顶点的距离(此时S中只有v0,故不存在中转顶点);
  2. v0到达顶点vk的距离最小,则将vk加入到集合S中,此时多了一个中转顶点vk
  3. 所以要对剩余的各个顶点的最短路径长度进行更新
  4. 原来v0到vi的最短路径长度为D[i],加入k作为中间顶点的中转路径长度为:D[k]+Garcs[k][i],若D[k]+Garcs[k][i]<D[i],则用D[k]+Garcs[k][i]取代D[i]。
  5. 更新后,再选择数组D中值最小的顶点加入到顶点集S中,如此进行下去,直至图中所有顶点加入到顶点集S为止。

 

 

 

 四、弗洛伊德算法

计算的是 每个顶点之间的最短路径。 

 

 

### Floyd-Warshall算法简介 Floyd-Warshall算法是一种用于解决多源最短路径问题的经典动态规划方法[^2]。该算法能够处理带有负权重边的图结构,只要不存在总权值为负数的环路即可正常工作。 此算法通过逐步增加中间顶点来构建更广泛的连接关系,并终确定任意两点间的小距离。对于给定的一个加权有向图G=(V,E),其中|V|=n,则可以定义一个三维数组D[i][j][k]表示从i到j仅经过编号不大于k的节点作为中介时两者的最短路径长度;当k=0时意味着不允许任何其他节点充当媒介物。 随着循环变量k的增长,在每一轮迭代过程中都会更新当前已知的佳路线直到遍历完所有的可能选项为止。具体来说就是: - 如果存在一条直接相连的道路d(i,j); - 或者经由某个特定中转k使得新的路程变得更短:min{ d(i,k)+d(k,j), D[i][j]}。 这样做的好处在于每次只考虑新增加的那个结点所带来的影响,从而简化了整体逻辑并提高了效率。 ### 实现过程 以下是Python实现版本的Floyd-Warshall算法代码示例: ```python def floyd_warshall(graph): V = len(graph) dist = [[float('inf')]*V for _ in range(V)] # 初始化邻接矩阵 for i in range(V): for j in range(V): if graph[i][j]: dist[i][j] = graph[i][j] # 对角线元素设为0 for k in range(V): dist[k][k] = 0 # 动态规划最短路径 for k in range(V): for i in range(V): for j in range(V): if dist[i][j]>dist[i][k]+dist[k][j]: dist[i][j]=dist[i][k]+dist[k][j] return dist ``` 上述程序接收输入参数`graph`代表有权重的邻接矩阵形式描述的一张图,返回结果同样是以二维列表的形式给出各个顶点之间的最短路径长度表。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值