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

题目

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

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

在这里插入图片描述

代码

class Solution {
    // 哈希表记录每个点的入度
    // to -> [from, price]
    HashMap<Integer, List<int[]>> indegree;
    // 备忘录
    int[][] memo;

    int src, dst;
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
        // 将中转站个数转化成边的条数
        k++;
        this.src=src;
        this.dst=dst;
        memo= new int[n][k+1];
        for (int[] row:memo
             ) {
            Arrays.fill(row,-999);
        }

        indegree = new HashMap<>();
        for (int[] f:flights
             ) {
            int from =f[0];
            int to =f[1];
            int price=f[2];
            // 记录谁指向该节点,以及之间的权重,求入度
            indegree.putIfAbsent(to,new LinkedList<>());
            indegree.get(to).add(new int[]{from,price});

        }
        return dp(dst,k);
    }

    // 定义:从 src 出发,k 步之内到达 s 的最短路径权重
    int dp(int s,int k){
//        base case
        if (s==src) return 0;
        if (k==0) return -1;//已经走不通了
        if(memo[s][k]!=-999) return memo[s][k];

        // 初始化为最大值,方便等会取最小值
        int res=Integer.MAX_VALUE;
        if (indegree.containsKey(s)){ // 当 s 有入度节点时,分解为子问题
            for (int[] v:indegree.get(s)
                 ) {
                int from=v[0];
                int price=v[1];
                // 从 src 到达相邻的入度节点所需的最短路径权重
                int subProb=dp(from,k-1);
                //跳过无解
                if (subProb!=-1){
                    res = Math.min(res,subProb+price);
                }

            }
        }
        memo[s][k]=res==Integer.MAX_VALUE? -1:res;
        return memo[s][k];

    }
}

要点

  • 本题使用递归解法,从目的地开始处理,将问题缩小为往出发点的子问题
  • 由于递归方向是往出发点,故需要记录顶点的入度,使用哈希map来记录顶点入度
  • 最后使用记忆表来加快处理速度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值