|
| 1 | +### 题目描述 |
| 2 | + |
| 3 | +这是 LeetCode 上的 **[2039. 网络空闲的时刻](https://leetcode-cn.com/problems/the-time-when-the-network-becomes-idle/solution/by-ac_oier-5txs/)** ,难度为 **中等**。 |
| 4 | + |
| 5 | +Tag : 「BFS」 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +给你一个有 $n$ 个服务器的计算机网络,服务器编号为 0ドル$ 到 $n - 1$ 。同时给你一个二维整数数组 `edges` ,其中 $edges[i] = [u_i, v_i]$ 表示服务器 $u_i$ 和 $v_i$ 之间有一条信息线路,在一秒内它们之间可以传输任意数目的信息。再给你一个长度为 $n$ 且下标从 0ドル$ 开始的整数数组 `patience` 。 |
| 10 | + |
| 11 | +题目保证所有服务器都是相通的,也就是说一个信息从任意服务器出发,都可以通过这些信息线路直接或间接地到达任何其他服务器。 |
| 12 | + |
| 13 | +编号为 0ドル$ 的服务器是主服务器,其他服务器为数据服务器。每个数据服务器都要向主服务器发送信息,并等待回复。信息在服务器之间按最优线路传输,也就是说每个信息都会以最少时间到达主服务器。主服务器会处理所有新到达的信息并立即按照每条信息来时的路线反方向发送回复信息。 |
| 14 | + |
| 15 | +在 0ドル$ 秒的开始,所有数据服务器都会发送各自需要处理的信息。从第 1ドル$ 秒开始,每一秒最开始时,每个数据服务器都会检查它是否收到了主服务器的回复信息(包括新发出信息的回复信息): |
| 16 | + |
| 17 | +* 如果还没收到任何回复信息,那么该服务器会周期性重发信息。数据服务器 $i$ 每 $patience[i]$ 秒都会重发一条信息,也就是说,数据服务器 $i$ 在上一次发送信息给主服务器后的 $patience[i]$ 秒 后 会重发一条信息给主服务器。 |
| 18 | +* 否则,该数据服务器不会重发信息。 |
| 19 | + |
| 20 | +当没有任何信息在线路上传输或者到达某服务器时,该计算机网络变为空闲状态。 |
| 21 | + |
| 22 | +请返回计算机网络变为空闲状态的最早秒数。 |
| 23 | + |
| 24 | +示例 1: |
| 25 | + |
| 26 | +``` |
| 27 | +输入:edges = [[0,1],[1,2]], patience = [0,2,1] |
| 28 | + |
| 29 | +输出:8 |
| 30 | + |
| 31 | +解释: |
| 32 | +0 秒最开始时, |
| 33 | +- 数据服务器 1 给主服务器发出信息(用 1A 表示)。 |
| 34 | +- 数据服务器 2 给主服务器发出信息(用 2A 表示)。 |
| 35 | + |
| 36 | +1 秒时, |
| 37 | +- 信息 1A 到达主服务器,主服务器立刻处理信息 1A 并发出 1A 的回复信息。 |
| 38 | +- 数据服务器 1 还没收到任何回复。距离上次发出信息过去了 1 秒(1 < patience[1] = 2),所以不会重发信息。 |
| 39 | +- 数据服务器 2 还没收到任何回复。距离上次发出信息过去了 1 秒(1 == patience[2] = 1),所以它重发一条信息(用 2B 表示)。 |
| 40 | + |
| 41 | +2 秒时, |
| 42 | +- 回复信息 1A 到达服务器 1 ,服务器 1 不会再重发信息。 |
| 43 | +- 信息 2A 到达主服务器,主服务器立刻处理信息 2A 并发出 2A 的回复信息。 |
| 44 | +- 服务器 2 重发一条信息(用 2C 表示)。 |
| 45 | +... |
| 46 | +4 秒时, |
| 47 | +- 回复信息 2A 到达服务器 2 ,服务器 2 不会再重发信息。 |
| 48 | +... |
| 49 | +7 秒时,回复信息 2D 到达服务器 2 。 |
| 50 | + |
| 51 | +从第 8 秒开始,不再有任何信息在服务器之间传输,也不再有信息到达服务器。 |
| 52 | +所以第 8 秒是网络变空闲的最早时刻。 |
| 53 | +``` |
| 54 | +示例 2: |
| 55 | + |
| 56 | +``` |
| 57 | +输入:edges = [[0,1],[0,2],[1,2]], patience = [0,10,10] |
| 58 | + |
| 59 | +输出:3 |
| 60 | + |
| 61 | +解释:数据服务器 1 和 2 第 2 秒初收到回复信息。 |
| 62 | +从第 3 秒开始,网络变空闲。 |
| 63 | +``` |
| 64 | + |
| 65 | +提示: |
| 66 | +* $n == patience.length$ |
| 67 | +* 2ドル <= n <= 10^5$ |
| 68 | +* $patience[0] == 0$ |
| 69 | +* 对于 1ドル <= i < n$ ,满足 1ドル <= patience[i] <= 10^5$ |
| 70 | +* 1ドル <= edges.length <= \min(105, n * (n - 1) / 2)$ |
| 71 | +* $edges[i].length == 2$ |
| 72 | +* 0ドル <= ui, vi < n$ |
| 73 | +* $ui != vi$ |
| 74 | +* 不会有重边。 |
| 75 | +* 每个服务器都直接或间接与别的服务器相连。 |
| 76 | + |
| 77 | +--- |
| 78 | + |
| 79 | +### 建图 + BFS |
| 80 | + |
| 81 | +根据题目可知这是一个边权为 1ドル$ 的无向连通图,我们可以采用「邻接表建图 + BFS」的方式预处理出 $dist$ 数组,$dist[i]$ 含义为节点 $i$ 到 0ドル$ 号点的最短距离。 |
| 82 | + |
| 83 | +一个数据服务器 $i$ 往主服务器发送消息所消耗的时间为两节点之间的最短路径 $dist[i],ドル而从发送消息到收到回复所需的时间为 $di = 2 * dist[i]$。 |
| 84 | + |
| 85 | +同时每个数据服务器还存在时间间隔为 $t = patience[i]$ 的重发动作,并且动作只有在第一次收到主服务的回复后才会停止。 |
| 86 | + |
| 87 | +因此如果 $di <= t,ドル那么数据服务器不会发生重发动作,该节点活动停止时间点为 $di$;当 $di > t,ドル数据服务器将会发生重发动作,且最后一个数据包的发送时间为 $(di - 1) / t * t,ドル只有当最后一个数据包收到回复,该节点的所有活动才算停止,停止时间点为 $(di - 1) / t * t + di$。所有节点的活动停止时间点的最大值即是答案。 |
| 88 | + |
| 89 | +代码: |
| 90 | +```Java |
| 91 | +class Solution { |
| 92 | + static int N = 100010, M = N * 2, INF = 0x3f3f3f3f; |
| 93 | + static int[] he = new int[N], e = new int[M], ne = new int[M]; |
| 94 | + static int[] dist = new int[N]; |
| 95 | + int idx; |
| 96 | + void add(int a, int b) { |
| 97 | + e[idx] = b; |
| 98 | + ne[idx] = he[a]; |
| 99 | + he[a] = idx++; |
| 100 | + } |
| 101 | + public int networkBecomesIdle(int[][] edges, int[] patience) { |
| 102 | + Arrays.fill(he, -1); |
| 103 | + Arrays.fill(dist, INF); |
| 104 | + int n = patience.length; |
| 105 | + for (int[] e : edges) { |
| 106 | + add(e[0], e[1]); |
| 107 | + add(e[1], e[0]); |
| 108 | + } |
| 109 | + Deque<Integer> d = new ArrayDeque<>(); |
| 110 | + d.addLast(0); |
| 111 | + dist[0] = 0; |
| 112 | + while (!d.isEmpty()) { |
| 113 | + int t = d.pollFirst(); |
| 114 | + for (int i = he[t]; i != -1; i = ne[i]) { |
| 115 | + int j = e[i]; |
| 116 | + if (dist[j] != INF) continue; |
| 117 | + dist[j] = dist[t] + 1; |
| 118 | + d.addLast(j); |
| 119 | + } |
| 120 | + } |
| 121 | + int ans = -1; |
| 122 | + for (int i = 1; i < n; i++) { |
| 123 | + int di = dist[i] * 2, t = patience[i]; |
| 124 | + int cur = di <= t ? di : (di - 1) / t * t + di; |
| 125 | + if (ans == -1 || cur > ans) ans = cur; |
| 126 | + } |
| 127 | + return ans + 1; |
| 128 | + } |
| 129 | +} |
| 130 | +``` |
| 131 | +* 时间复杂度:$O(n + m)$ |
| 132 | +* 空间复杂度:$O(n + m)$ |
| 133 | + |
| 134 | +--- |
| 135 | + |
| 136 | +### 最后 |
| 137 | + |
| 138 | +这是我们「刷穿 LeetCode」系列文章的第 `No.2039` 篇,系列开始于 2021年01月01日,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 |
| 139 | + |
| 140 | +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 |
| 141 | + |
| 142 | +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 |
| 143 | + |
| 144 | +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 |
| 145 | + |
0 commit comments