「这是我参与2022首次更文挑战的第40天,活动详情查看:2022首次更文挑战」。
题目
有 n 个城市通过一些航班连接。给你一个数组 flights ,其中 flights[i] = [fromi, toi, pricei] ,表示该航班都从城市 fromi 开始,以价格 pricei 抵达 toi。
现在给定所有的城市和航班,以及出发城市 src 和目的地 dst,你的任务是找到出一条最多经过 k 站中转的路线,使得从 src 到 dst 的 价格最便宜 ,并返回该价格。 如果不存在这样的路线,则输出 -1。
示例 1:
输入:n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 1
输出:200
解释:从城市 0 到城市 2 在 1 站中转以内的最便宜价格是 200,如图中红色所示。
示例 2:
输入:n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 0
输出:500
解释:从城市 0 到城市 2 在 0 站中转以内的最便宜价格是 500,如图中蓝色所示。
思路
这是一类求解最短路径的动态规划,也可以拆分成动态规划的几个常规步骤,不过定义状态需要一些经验。
状态定义
定义一个二维数组dp,dp[i][j]的含义是,从src出发,恰好经过i个航班,达到目的地的j的最小花费。那么我们最终要求的解就是min(dp[][dst])
转移方程
如果存在从start到end的航班,花费为cost,那么dp[i][j]可以选择采用或者不采用这条航线,分成2种情况 采用,dp[i][end] = dp[i-1][start] + cost 不采用,dp[i][end] = dp[i][end] 状态转移方程可以表达为
dp[i][end] = min(dp[i][end], dp[i-1][start] + cost)
边界条件
显然,如果不经过航班,只有dp[0][src] = 0。其他的初始值,都可以给一个足够大的值max,因为最终我们求解的是min(dp[][dst])。那max怎么取值合适呢?首先想到的是Integer.MAX_VALUE,但是后面加cost就会溢出,所以我们根据条件限制,每个航班的cost不会超过10000,最多经过k次中装,就是k+1个航班,所以理论上费用的最大值就是10000*(k+1),所以我们令
max = 10000 * (k+1) + 1;
就可以起到一个不能达到的大数的效果。
Java版本代码
class Solution {
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
int max = 10000 * (k+1) + 1;
int[][] dp = new int[k+2][n];
for (int[] item : dp) {
Arrays.fill(item, max);
}
dp[0][src] = 0;
for (int i = 1; i <= k+1; i++) {
for (int[] flight : flights) {
int start = flight[0];
int end = flight[1];
int cost = flight[2];
dp[i][end] = Integer.min(dp[i][end], dp[i-1][start] + cost);
}
}
int ans = max;
for (int i = 1; i <= k+1; i++) {
ans = Integer.min(ans, dp[i][dst]);
}
if (ans == max) {
return -1;
}
return ans;
}
}