题目
有 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来记录顶点入度
- 最后使用记忆表来加快处理速度