leetcode-K 站中转内最便宜的航班

237 阅读3分钟

「这是我参与2022首次更文挑战的第40天,活动详情查看:2022首次更文挑战」。

题目

有 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,如图中红色所示。
示例1.png

示例 2:
输入:n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 0
输出:500
解释:从城市 0 到城市 2 在 0 站中转以内的最便宜价格是 500,如图中蓝色所示。
示例2.png

思路

这是一类求解最短路径的动态规划,也可以拆分成动态规划的几个常规步骤,不过定义状态需要一些经验。

状态定义

定义一个二维数组dp,dp[i][j]的含义是,从src出发,恰好经过i个航班,达到目的地的j的最小花费。那么我们最终要求的解就是min(dp[][dst])

转移方程

如果存在从start到end的航班,花费为cost,那么dp[i][j]可以选择采用或者不采用这条航线,分成2种情况 采用,dp[i][end] = dp[i-1][start] + cost 不采用,dp[i][end] = dp[i][end] 状态转移方程可以表达为

dp[i][end] = min(dp[i][end], dp[i-1][start] + cost)
边界条件

显然,如果不经过航班,只有dp[0][src] = 0。其他的初始值,都可以给一个足够大的值max,因为最终我们求解的是min(dp[][dst])。那max怎么取值合适呢?首先想到的是Integer.MAX_VALUE,但是后面加cost就会溢出,所以我们根据条件限制,每个航班的cost不会超过10000,最多经过k次中装,就是k+1个航班,所以理论上费用的最大值就是10000*(k+1),所以我们令

max = 10000 * (k+1) + 1;

就可以起到一个不能达到的大数的效果。

Java版本代码

class Solution {
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
        int max = 10000 * (k+1) + 1;
        int[][] dp = new int[k+2][n];
        for (int[] item : dp) {
            Arrays.fill(item, max);
        }
        dp[0][src] = 0;
        for (int i = 1; i <= k+1; i++) {
            for (int[] flight : flights) {
                int start = flight[0];
                int end = flight[1];
                int cost = flight[2];
                dp[i][end] = Integer.min(dp[i][end], dp[i-1][start] + cost);
            }
        }
        int ans = max;
        for (int i = 1; i <= k+1; i++) {
            ans = Integer.min(ans, dp[i][dst]);
        }
        if (ans == max) {
            return -1;
        }
        return ans;
    }
}