leetcode 787 K 站中最便宜的航班 DP

Posted 牛有肉

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode 787 K 站中最便宜的航班 DP相关的知识,希望对你有一定的参考价值。

  状态转移方程的定义为:dp( K,i ) 表示经历 K 站乘坐到 flight[i] 航班终点的最低票价。

  因为 flight 中的元素存在前后置关系,所以乘坐某航班的上一航班的集合是可以确定的。

  dp( K,i ) = Math.min( dp( K-1,j ) ),其中 j 为可以作为上一趟航班的航班下标集合。

/**
     * @Author Niuxy
     * @Date 2020/10/18 4:53 下午
     * @Description 按出发地选中开头航班后,不断通过比较目的地与下一出发地寻找可能的下一航班
     * 因为每趟航班的目的地是确定的,不管从哪条路线来到该航班,最终结果只受剩余可乘坐航班次数的限制
     * 因此可以通过航班坐标 flag 及剩余可乘坐航班次数 K 来进行结果的缓存
     * 那么可以以这两个维度进行 DP 递推
     */
    public final int findCheapestPriceDP(int n, int[][] flights, int src, int dst, int K) {
        int len = flights.length;
        int[][] cache = new int[K + 1][len];
        int re = Integer.MAX_VALUE;
        for (int i = 0; i < cache[0].length; i++) {
            if (flights[i][0] != src) continue;
            if (flights[i][1] == dst) re = Math.min(re, flights[i][2]);
            else cache[K][i] = flights[i][2];
        }
        for (int i = K - 1; i >= 0; i--) {
            for (int j = 0; j < len; j++) {
                int minPreCost = Integer.MAX_VALUE;
                for (int k = 0; k < len; k++) {
                    if (cache[i + 1][k] == 0 || flights[j][0] != flights[k][1]) continue;
                    else minPreCost = Math.min(minPreCost, cache[i + 1][k]);
                }
                if (minPreCost != Integer.MAX_VALUE)
                    if (flights[j][1] == dst) re = Math.min(re, minPreCost + flights[j][2]);
                    else cache[i][j] = minPreCost + flights[j][2];
            }
        }
        return re == Integer.MAX_VALUE ? -1 : re;
    }

  在第三层 for 循环中,我们在以 O(N) 的时间复杂度寻找上一趟航班可能的下标。整个状态转移方程没有用到参数 n 。

  如果以 n 为维度建立缓存表,遍历 flight 时可以直接以 O(1) 的时间复杂度找到上一趟航班;另外,DP 的过程只涉及到 K 与 K-1 行,可以借此优化空间复杂度:

    public final int findCheapestPriceDP2(int n, int[][] flights, int src, int dst, int K) {
        int flag = Integer.MAX_VALUE / 2;
        int[][] dp = new int[2][n];
        dp[0][src] = dp[1][src] = 0;
        Arrays.fill(dp[0], flag);
        Arrays.fill(dp[1], flag);
        for (int k = 0; k <= K; k++) {
            for (int[] info : flights) {
                dp[k & 1][info[1]] = Math.min(dp[k & 1][info[1]], dp[~k & 1][info[0]] + info[2]);
            }
        }
        return dp[K & 1][dst] < Integer.MAX_VALUE / 2 ? dp[K & 1][dst] : -1;
    }

  JS 写法:

/**
 * @param {number} n
 * @param {number[][]} flights
 * @param {number} src
 * @param {number} dst
 * @param {number} K
 * @return {number}
 */
var findCheapestPrice = function (n, flights, src, dst, K) {
    let dp = [];
    for (let i = 0; i < K + 2; i++) {
        dp.push(new Array(n).fill(Number.MAX_VALUE));
        dp[i][src] = 0;
    }
    let an = Number.MAX_VALUE;
    for (let i = 1; i < K + 2; i++) {
        for (let j = 0; j < flights.length; j++) {
            dp[i][flights[j][1]] = Math.min(dp[i][flights[j][1]], dp[i - 1][flights[j][0]] + flights[j][2]);
        }
    }
    return dp[K + 1][dst] == Number.MAX_VALUE ? -1 : dp[K + 1][dst];
};

 

 

  

以上是关于leetcode 787 K 站中最便宜的航班 DP的主要内容,如果未能解决你的问题,请参考以下文章

[leetcode] 787 K 站中转内最便宜的航班

LeetCode 787 K站中转内最便宜的航班[动态规划] HERODING的LeetCode之路

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

LeetCode 789. 逃脱阻碍者(贪心) / 1646. 获取生成数组中的最大值 / 787. K 站中转内最便宜的航班(有限制的最短路,重新审视迪杰斯特拉,动态规划)

缺迪杰斯特拉和SPFA] 文巾解题 787. K 站中转内最便宜的航班

[M最短路] lc787. K 站中转内最便宜的航班(Bellman-Ford算法模板+边数限制最短路+dp思想)