题目来源:https://leetcode-cn.com/problems/the-time-when-the-network-becomes-idle/
大致题意:
给 n 个节点(编号0 ~ n-1)表示路由器节点,一个二维数组表示相连的节点,一个数组表示每个节点的耐心时间
用 0 节点表示服务器,从 0 秒开始,所有路由器向服务器发送消息,每秒消息只能移动到相邻节点,服务器收到消息后原路返回给节点。每秒开始时,路由器都检查是否收到回复,若没有且当前时间超过了耐心时间,那么重新发送消息;若收到了则不再发送消息
假设消息发送都经过最优路径,求最短在第几秒时,整个网络没有消息
思路
遍历求出每个路由器的消息在网络上的时间,取最长的那个时间 + 1 即为所求秒数
因为需要发送时间尽可能地短,所以消息都经过最短路径发送,这也就是一个边权值相同的最短路径问题,于是可以用 BFS
BFS
- 求出每个节点相连的边
- 以 0 为源点,开始 BFS
- 对于搜索的每个节点,求出其消息在网络中滞留的时间,并统计最大值
- 最大值 + 1 即为网络上没有消息的时间
节点消息在网络上滞留的时间为:
- 节点与源点的最短路径长度 * 2 + 节点最后一次发送消息的时间
使用 time 表示节点与源点的最短路径长度 * 2,使用 p 表示该节点对应的耐心时间,则有:
- 若 time % p = 0,那么可以先求出重发次数:time / p,因为每秒开始时先检查是否有回复,所以在 time 秒时没有发送消息,那么最后一次发送消息的时间为
(time / p - 1) * p
- 否则,其为 重发次数 * p,即
time / p * p
public int networkBecomesIdle(int[][] edges, int[] patience) {
Set<Integer> vis = new HashSet<>(); // 标记访问过的节点
Map<Integer, List<Integer>> map = new HashMap<>(); // 存每个节点相连的节点
Queue<int[]> queue = new ArrayDeque<>(); // BFS 的队列
int n = patience.length;
// 遍历获取所有节点的相邻节点
for (int[] edge : edges) {
int x = edge[0];
int y = edge[1];
List<Integer> xList = map.getOrDefault(x, new ArrayList<>());
xList.add(y);
map.put(x, xList);
List<Integer> yList = map.getOrDefault(y, new ArrayList<>());
yList.add(x);
map.put(y, yList);
}
// 源点入队
// 两个值,第一个表示节点编号,第二个表示其与源点的最短距离
queue.offer(new int[]{0, 0});
vis.add(0);
int ans = Integer.MIN_VALUE;
while (!queue.isEmpty()) {
// 出队
int[] cur = queue.poll();
// 获取节点
int x = cur[0];
if (x != 0) {
// 获取最短的来回时间
int curTime = cur[1] * 2;
// 求其消息在网络上的滞留时间
if (curTime % patience[x] == 0) {
curTime += (curTime / patience[x] - 1) * patience[x];
} else {
curTime += (curTime / patience[x]) * patience[x];
}
// 更新最大值
ans = Math.max(ans, curTime);
}
List<Integer> list = map.get(x);
// 将相邻未访问的点入队列
for (int to : list) {
if (!vis.contains(to)) {
vis.add(to);
queue.offer(new int[]{to, cur[1] + 1});
}
}
}
return ans + 1;
}