难度:困难
目录
一、问题描述
这里直接采用的是LeetCode上面的问题描述。
城市用一个 双向连通 图表示,图中有 n 个节点,从 1 到 n 编号(包含 1 和 n)。图中的边用一个二维整数数组 edges 表示,其中每个 edges[i] = [ui, vi] 表示一条节点 ui 和节点 vi 之间的双向连通边。每组节点对由 最多一条 边连通,顶点不存在连接到自身的边。穿过任意一条边的时间是 time 分钟。
每个节点都有一个交通信号灯,每 change 分钟改变一次,从绿色变成红色,再由红色变成绿色,循环往复。所有信号灯都 同时 改变。你可以在 任何时候 进入某个节点,但是 只能 在节点 信号灯是绿色时 才能离开。如果信号灯是 绿色 ,你 不能 在节点等待,必须离开。
第二小的值 是 严格大于 最小值的所有值中最小的值。
- 例如,[2, 3, 4] 中第二小的值是 3 ,而 [2, 2, 4] 中第二小的值是 4 。给你 n、edges、time 和 change ,返回从节点 1 到节点 n 需要的 第二短时间 。
注意:
- 你可以 任意次 穿过任意顶点,包括 1 和 n 。
- 你可以假设在 启程时 ,所有信号灯刚刚变成 绿色 。
下面给出示例:
提示:
- 2 <= n <= 104
- n - 1 <= edges.length <= min(2 * 104, n * (n - 1) / 2)
- edges[i].length == 2
- 1 <= ui, vi <= n
- ui != vi
- 不含重复边
- 每个节点都可以从其他节点直接或者间接到达
- 1 <= time, change <= 103
二、解题思想
首先看到这题,关于图的最小路径,首先想到的就是DFS以及BFS。但是这一题不是求图的最短路径而是求图的 次短路径 的 时间。和之前的1345.跳跃游戏IV-LeetCode有异曲同工之妙,那一题是求到最后一个节点的最短路径,此题是求 次短路径 的 时间。
这里需要注意的是题目中说了:你可以 任意次 穿过任意顶点,包括 1 和 n 。意思就是可以回头穿过源点,再去走目标节点。意思为下图,当节点为 2 时,需要穿过源点一次此时为 次短路径。
我们依然可以用BFS,在这里需要维护一个从源点到其他节点的最短路径和 次短路径 的表。
使用BFS之前要做的准备:
- 初始化vector<vector<int>> path(n+1, vector<int>(2, INT_MAX)) ( path[i][0] 存放从源点到i节点的 最短路径长度,path[i][1] 存放从源点到 i 节点的 严格次短路径长度 )。将path[1][0] 的距离设置为 0。
- 定义队列 queue<pair<int, int>> Q (队列 Q{ i, j } 用来存储源点到节点 i 的 路程 j)。入队{1,0}从源点开始 BFS。
BFS需要进行的操作:
- 判断 源点 到 当前节点的路径 是否 严格小于 最小路径,维护最短路径path[i,0]。( i 为当前BFS的节点)
- 判断 源点 到 当前节点的路径 是否 严格大于 最小路径 且 严格小于 次短路径 ,维护次短路径path[i][1]。( i 为当前BFS的节点)
最后要进行将 次短路径 转换为次短时间。由于刚起始是绿灯,那么每过 2*change 又是绿灯;则只有在当前节点 的时间处于 :ans % (2 * change) >= change 那么就需要等待的时间为:(2 * change - ans % (2 * change)) 。
三、解题
1、判断极端情况
这里使用BFS不需要判断极端情况最少传入两个节点。
2、代码实现
class Solution {
public:
int secondMinimum(int n, vector<vector<int>>& edges, int time, int change) {
//用零阶矩阵 将无向图抽象出来
vector<vector<int>> Graphs(n+1);
for(auto& item: edges){
Graphs[item[0]].push_back(item[1]);
Graphs[item[1]].push_back(item[0]);
}
// path[i][0] 存放从源点到i节点的 最短路径长度,path[i][1] 存放从源点到i节点的 严格次短路径长度
vector<vector<int>> path(n+1, vector<int>(2, INT_MAX));
path[1][0] = 0;
//队列 Q{ i, j } 用来存储源点到节点 i 的 路程 j
queue<pair<int, int>> Q;
//初始化从节点1 开始BFS
Q.push({1,0});
//使用BFS 一直计算从源点 到各节点的 最小以及次小路径 当有了目的节点的次小路径之后 停止循环
while(path[n][1] == INT_MAX){
//p用来保存 源点到节点 i 的 路程 j
auto p = Q.front();
Q.pop();
//bfsNode 为每一轮广度搜索到达的节点 更新 从原点到达bfsNode节点 的 最小及次小路径
for(auto bfsNode : Graphs[p.first]){
//如果从源点到达bfsNode 的路径 小于当前最小路径 那么更新此最小路径
if(p.second + 1 < path[bfsNode][0]){
path[bfsNode][0] = p.second + 1;
Q.push({bfsNode, p.second + 1});
}
//如果从源点到达bfsNode 的路径 严格大于最小路径 并且严格小于当前次小路径 那么更新此次小路径
if(p.second + 1 > path[bfsNode][0] && p.second + 1 < path[bfsNode][1]){
path[bfsNode][1] = p.second + 1;
Q.push({bfsNode, p.second + 1});
}
}
}
int ans = 0;
//刚起始是绿灯,每过 2*change 又是绿灯
//ans是当前总时间 ans %(2*change) >= change 则是需要等待的时间 小于则不需要等待
//(2 * change - ans % (2 * change )) 则是需要等待的时间
for(int i = 0; i < path[n][1]; i++){
if(ans % (2 * change) >= change){
ans += (2 * change - ans % (2 * change));
}
ans += time;
}
return ans;
}
};
四、总结
刚开始看到题目,是懵逼的(基础比较薄弱),然后在分析题解一步步将题解拆开看,开始没有弄明白 队列中 pair 参数的意思,瞎琢磨了很久,后面可以将无向图想象成双向图,只要没有找到次短路径 那么就一直BFS以增加路径长度,直至找到 次短路径 为止。
注释都很详细,如果对你有什么帮助,请star ♥ 一下,收藏一下,蟹蟹啦!👇👇