难度中等317
有 n
个城市通过一些航班连接。给你一个数组 flights
,其中 flights[i] = [fromi, toi, pricei]
,表示该航班都从城市 fromi
开始,以价格 toi
抵达 pricei
。
现在给定所有的城市和航班,以及出发城市 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,如图中蓝色所示。
提示:
1 <= n <= 100
0 <= flights.length <= (n * (n - 1) / 2)
flights[i].length == 3
0 <= fromi, toi < n
fromi != toi
1 <= pricei <= 104
- 航班没有重复,且不存在自环
0 <= src, dst, k < n
src != dst
思路:首先需要转换一下题意,经过k个中转到达dst,可以等价为走过了k+1步到达dst。定义dp[src_i][k_t]表示当前处于src,且已经走了k_t步的最小代价,那么假如有src_i->src_j,就可以知道dp[src_j][k_t+1] = min(dp[src_j][k_t+1], dp[src_i][k_t] + price_i_j])。
[注]:本题有个大坑,因为我想用的是每次保存src_i,k_t,用于更新src_j,k_t+1,也就是正向生成,而不是反推当前状态由哪些状态可以转移得到,因此在转移后需要将状态入队列供后续使用,但是!!请注意,对于入节点的状态需要进行标记,否则可能会入多次。
例如,src_i->src_j且src_z->src_j,那么dp[src_i][k_t]和dp[src_z][k_t]就会同时对dp[src_j][k_t+1]进行更新,也就是说src_j、k_t+1会入队列多次,会超时,因此可以用一个标记数组防止重复入队列。
class Solution {
public:
int Next[5000], To[5000], Head[110], W[5000], cnt, dp[110][110];
bool mark[110][110];
struct Point{
int src, tempk;
};
void add(int from, int to, int price) {
W[cnt] = price;
To[cnt] = to;
Next[cnt] = Head[from];
Head[from] = cnt ++;
}
int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int k) {
memset(Next, -1, sizeof(Next));
memset(To, -1, sizeof(To));
memset(Head, -1, sizeof(Head));
memset(W, -1, sizeof(W));
memset(mark, 0, sizeof(mark));
cnt = 0;
int m = flights.size(), minValue = 0x3f3f3f3f, to, price, tempk, i;
for(i = 0; i < m; ++ i) {
add(flights[i][0], flights[i][1], flights[i][2]);
}
memset(dp, 0x3f, sizeof(dp));
dp[src][0] = 0;
queue<Point> que;
que.push(Point{src, 0});
Point point;
while(!que.empty()) {
point = que.front();
que.pop();
src = point.src; tempk = point.tempk;
if(src == dst) {
minValue = min(minValue, dp[src][tempk]);
}
if(tempk >= k + 1) { //最多k个中转 等价于 最多 第k + 1班到达目的地
continue;
}
for(i = Head[src]; i != -1; i = Next[i]) {
to = To[i];
price = W[i];
dp[to][tempk + 1] = min(dp[to][tempk + 1], dp[src][tempk] + price);
if(tempk >= k && to != dst) continue;
if(!mark[to][tempk + 1]) {
mark[to][tempk + 1] = true;
que.push(Point{to, tempk + 1});
}
}
}
return minValue == 0x3f3f3f3f ? -1 : minValue;
}
};