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];
};
复制代码

 

 

  

posted @   牛有肉  阅读(167)  评论(0编辑  收藏  举报
编辑推荐:
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示