leetcode787. K 站中转内最便宜的航班
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/cheapest-flights-within-k-stops
题目描述
有 n 个城市通过一些航班连接。给你一个数组 flights ,其中 flights[i] = [fromi, toi, pricei] ,表示该航班都从城市 fromi 开始,以价格 pricei 抵达 toi。
现在给定所有的城市和航班,以及出发城市 src 和目的地 dst,你的任务是找到出一条最多经过 k 站中转的路线,使得从 src 到 dst 的 价格最便宜 ,并返回该价格。 如果不存在这样的路线,则输出 -1。
暴力递归
如果用递归来解的话.这题就类似于最小路径和问题,同样的套路也可以用在这个问题上,如图演示:
s1, s2 是指向 dst 的相邻节点,它们之间的权重我是知道的,分别是 w1, w2。
只要我知道了从 src 到 s1, s2 的最短路径,我不就知道 src 到 dst 的最短路径了吗!
这样就可以看出递归关系了:
minPath(src, dst) = min(
minPath(src, s1) + w1,
minPath(src, s2) + w2
)
我们要对原始数组做下处理,记录下可以到某个节点的所有所有线路和权重值,类似图算法的入度出度:
代码演示
HashMap<Integer, List<int[]>> graphMap;
//起始位置
int src;
/**
*
* @param n 城市数量
* @param flights 权重数组
* @param src 起始位置
* @param dst 终点位置
* @param K 要经过k个节点
* @return
*/
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
//边的条数 +1
this.src = src;
//要经过K 个节点的话,经过的线路就是K + 1
K++;
graphMap = new HashMap<>();
//记录所有能达到下一个节点的信息
for (int[] f : flights){
int from = f[0];
int to = f[1];
int price = f[2];
graphMap.putIfAbsent(to,new LinkedList<int[]>());
graphMap.get(to).add(new int[]{from,price});
}
return process(dst,K);
}
/**
*
* @param K 需要经过的边数
* @param dst 目标
* @return
*/
public int process(int dst, int K){
//base case 到达
if (dst == src){
return 0;
}
//没有机会了 返回 -1
if(K == 0){
return -1;
}
int res = Integer.MAX_VALUE;
if(graphMap.containsKey(dst)){
for (int[] load : graphMap.get(dst)){
int from = load[0];
int price = load[1];
//往前去递归
int sumMoney = process(from,K - 1);
if (sumMoney != -1){
//记录最小值
res = Math.min(res,sumMoney + price);
}
}
}
return res == Integer.MAX_VALUE ? -1 : res;
}
递归加缓存
消除重复计算的问题
//记录能到达某个节点的所有节点和权重值
HashMap<Integer, List<int[]>> graphMap;
//起始位置
int src;
/**
*
* @param n 城市数量
* @param flights 权重数组
* @param src 起始位置
* @param dst 终点位置
* @param K 要经过k个节点
* @return
*/
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
//边的条数 +1
this.src = src;
K++;
graphMap = new HashMap<>();
for (int[] f : flights){
int from = f[0];
int to = f[1];
int price = f[2];
graphMap.putIfAbsent(to,new LinkedList<int[]>());
graphMap.get(to).add(new int[]{from,price});
}
//缓存表
int[][]dp = new int[n][K + 1];
return process(dst,K,dp);
}
/**
*
* @param K 需要经过的边数
* @param dst 目标
* @return
*/
public int process(int dst, int K,int[][]dp){
if (dst == src){
return 0;
}
if(K == 0){
return -1;
}
//表里有数据直接拿
if(dp[dst][K] != 0){
return dp[dst][K];
}
int res = Integer.MAX_VALUE;
if(graphMap.containsKey(dst)){
for (int[] load : graphMap.get(dst)){
int from = load[0];
int price = load[1];
int sumMoney = process(from,K - 1,dp);
if (sumMoney != -1){
res = Math.min(res,sumMoney + price);
}
}
}
res = res == Integer.MAX_VALUE ? -1 : res;
//放进缓存表中
dp[dst][K] = res;
return res;
}