|
| 1 | +### 题目描述 |
| 2 | + |
| 3 | +这是 LeetCode 上的 **[1129. 颜色交替的最短路径]()** ,难度为 **中等**。 |
| 4 | + |
| 5 | +Tag : 「BFS」、「最短路」 |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +在一个有向图中,节点分别标记为 `0, 1, ..., n-1`。图中每条边为红色或者蓝色,且存在自环或平行边。 |
| 10 | + |
| 11 | +`red_edges` 中的每一个 `[i, j]` 对表示从节点 `i` 到节点 `j` 的红色有向边。类似地,`blue_edges` 中的每一个 `[i, j]` 对表示从节点 `i` 到节点 `j` 的蓝色有向边。 |
| 12 | + |
| 13 | +返回长度为 `n` 的数组 `answer`,其中 `answer[X]` 是从节点 `0` 到节点 `X` 的红色边和蓝色边交替出现的最短路径的长度。如果不存在这样的路径,那么 `answer[x] = -1`。 |
| 14 | + |
| 15 | +示例 1: |
| 16 | +``` |
| 17 | +输入:n = 3, red_edges = [[0,1],[1,2]], blue_edges = [] |
| 18 | +
|
| 19 | +输出:[0,1,-1] |
| 20 | +``` |
| 21 | +示例 2: |
| 22 | +``` |
| 23 | +输入:n = 3, red_edges = [[0,1]], blue_edges = [[2,1]] |
| 24 | +
|
| 25 | +输出:[0,1,-1] |
| 26 | +``` |
| 27 | +示例 3: |
| 28 | +``` |
| 29 | +输入:n = 3, red_edges = [[1,0]], blue_edges = [[2,1]] |
| 30 | +
|
| 31 | +输出:[0,-1,-1] |
| 32 | +``` |
| 33 | +示例 4: |
| 34 | +``` |
| 35 | +输入:n = 3, red_edges = [[0,1]], blue_edges = [[1,2]] |
| 36 | +
|
| 37 | +输出:[0,1,2] |
| 38 | +``` |
| 39 | +示例 5: |
| 40 | +``` |
| 41 | +输入:n = 3, red_edges = [[0,1],[0,2]], blue_edges = [[1,0]] |
| 42 | +
|
| 43 | +输出:[0,1,1] |
| 44 | +``` |
| 45 | + |
| 46 | +提示: |
| 47 | +* `1 <= n <= 100` |
| 48 | +* `red_edges.length <= 400` |
| 49 | +* `blue_edges.length <= 400` |
| 50 | +* `red_edges[i].length = blue_edges[i].length = 2` |
| 51 | +* `0 <= red_edges[i][j], blue_edges[i][j] < n` |
| 52 | + |
| 53 | +--- |
| 54 | + |
| 55 | +### 朴素 BFS |
| 56 | + |
| 57 | +为了方便,将 `redEdges` 记为 `rs`,将 `blueEdges` 记为 `bs`。 |
| 58 | + |
| 59 | +使用两数组进行建图,利用点数和边数关系(稀疏图),使用「邻接表」方式进行建图。 |
| 60 | + |
| 61 | +将所有红色有向边权值记为 `1`,将所有蓝色有向边权值记为 `-1`。注意这里的权重仅表示该边的颜色,并非代表经过该边的真实成本。 |
| 62 | + |
| 63 | +也正是经过所有边的成本均相同,对于原问题「从 `0` 号节点进行出发,求到所有点的颜色交替的最短路径」,我们容易想到使用 `BFS` 进行求解。 |
| 64 | + |
| 65 | +`BFS` 过程中将三元组 $(point, last, step)$ 进行入队: |
| 66 | + |
| 67 | +* `point` 代表当前所在点编号 |
| 68 | +* `last` 代表到达 `point` 所使用的边的颜色,默认为 `0` 代表不需要经过任何边到达起点 |
| 69 | +* `step` 代表到达 `point` 所使用的步数 |
| 70 | + |
| 71 | +利用数据范围不大(点数为 $100$,边数为 $800$),可以直接对每个点进行一次独立的 `BFS` 来求最短路。由于图存在重边和自环,因此在求解最短路的过程需要对边进行去重。 |
| 72 | + |
| 73 | +代码: |
| 74 | +```Java |
| 75 | +class Solution { |
| 76 | + static int N = 110, M = 810, idx = 0; |
| 77 | + static int[] he = new int[N], e = new int[M], ne = new int[M], w = new int[M]; |
| 78 | + void add(int a, int b, int c) { |
| 79 | + e[idx] = b; |
| 80 | + w[idx] = c; |
| 81 | + ne[idx] = he[a]; |
| 82 | + he[a] = idx++; |
| 83 | + } |
| 84 | + public int[] shortestAlternatingPaths(int n, int[][] rs, int[][] bs) { |
| 85 | + idx = 0; |
| 86 | + Arrays.fill(he, -1); |
| 87 | + for (int[] e : rs) add(e[0], e[1], 1); |
| 88 | + for (int[] e : bs) add(e[0], e[1], -1); |
| 89 | + int[] ans = new int[n]; |
| 90 | + boolean[] vis = new boolean[rs.length + bs.length]; |
| 91 | + out:for (int k = 1; k < n; k++) { |
| 92 | + ans[k] = -1; |
| 93 | + Arrays.fill(vis, false); |
| 94 | + Deque<int[]> d = new ArrayDeque<>(); |
| 95 | + d.addLast(new int[]{0, 0, 0}); // point, last, step |
| 96 | + while (!d.isEmpty()) { |
| 97 | + int[] info = d.pollFirst(); |
| 98 | + int p = info[0], last = info[1], step = info[2]; |
| 99 | + for (int i = he[p]; i != -1; i = ne[i]) { |
| 100 | + int j = e[i], c = w[i]; |
| 101 | + if (vis[i]) continue; |
| 102 | + if (c + last == 0 || last == 0) { |
| 103 | + if (j == k) { |
| 104 | + ans[k] = step + 1; |
| 105 | + continue out; |
| 106 | + } else { |
| 107 | + d.addLast(new int[]{j, c, step + 1}); |
| 108 | + vis[i] = true; |
| 109 | + } |
| 110 | + } |
| 111 | + } |
| 112 | + } |
| 113 | + } |
| 114 | + return ans; |
| 115 | + } |
| 116 | +} |
| 117 | +``` |
| 118 | +* 时间复杂度:将点数记为 `n` ,边数记为 `m`。建图复杂度为 $O(m)$;构造答案复杂度为 $O(n \times (n + m))$。整体复杂度为 $O(n \times (n + m))$ |
| 119 | +* 空间复杂度:$O(n + m)$ |
| 120 | + |
| 121 | +--- |
| 122 | + |
| 123 | +### 优化 |
| 124 | + |
| 125 | +实际上,我们并没有对每个点进行独立 `BFS` 的必要。 |
| 126 | + |
| 127 | +为了获取所有从节点 `0` 出发的最短路,可直接从节点 `0` 进行出发(对边进行去重),所有能够从节点 `0` 沿交替路径到达的节点必然都会被访问到,且节点首次被访问到时必然是最短路径。 |
| 128 | + |
| 129 | +因此我们只需要初始化所有的 `ans[i] = -1`,并从节点 `0` 开始进行一次 `BFS` 即可。 |
| 130 | + |
| 131 | +代码: |
| 132 | +```Java |
| 133 | +class Solution { |
| 134 | + static int N = 110, M = 810, idx = 0; |
| 135 | + static int[] he = new int[N], e = new int[M], ne = new int[M], w = new int[M]; |
| 136 | + void add(int a, int b, int c) { |
| 137 | + e[idx] = b; |
| 138 | + w[idx] = c; |
| 139 | + ne[idx] = he[a]; |
| 140 | + he[a] = idx++; |
| 141 | + } |
| 142 | + public int[] shortestAlternatingPaths(int n, int[][] rs, int[][] bs) { |
| 143 | + idx = 0; |
| 144 | + Arrays.fill(he, -1); |
| 145 | + for (int[] e : rs) add(e[0], e[1], 1); |
| 146 | + for (int[] e : bs) add(e[0], e[1], -1); |
| 147 | + int[] ans = new int[n]; |
| 148 | + Arrays.fill(ans, -1); |
| 149 | + ans[0] = 0; |
| 150 | + boolean[] vis = new boolean[rs.length + bs.length]; |
| 151 | + Arrays.fill(vis, false); |
| 152 | + Deque<int[]> d = new ArrayDeque<>(); |
| 153 | + d.addLast(new int[]{0, 0, 0}); // point, last, step |
| 154 | + while (!d.isEmpty()) { |
| 155 | + int[] info = d.pollFirst(); |
| 156 | + int p = info[0], last = info[1], step = info[2]; |
| 157 | + for (int i = he[p]; i != -1; i = ne[i]) { |
| 158 | + if (vis[i]) continue; |
| 159 | + int j = e[i], c = w[i]; |
| 160 | + if (c + last == 0 || last == 0) { |
| 161 | + ans[j] = ans[j] == -1 ? step + 1 : Math.min(ans[j], step + 1); |
| 162 | + d.addLast(new int[]{j, c, step + 1}); |
| 163 | + vis[i] = true; |
| 164 | + } |
| 165 | + } |
| 166 | + } |
| 167 | + return ans; |
| 168 | + } |
| 169 | +} |
| 170 | +``` |
| 171 | +* 时间复杂度:将点数记为 `n` ,边数记为 `m`。建图复杂度为 $O(m)$;构造答案复杂度为 $O(n + m)$。整体复杂度为 $O(n + m)$ |
| 172 | +* 空间复杂度:$O(n + m)$ |
| 173 | + |
| 174 | +--- |
| 175 | + |
| 176 | +### 最后 |
| 177 | + |
| 178 | +这是我们「刷穿 LeetCode」系列文章的第 `No.1224` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 |
| 179 | + |
| 180 | +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 |
| 181 | + |
| 182 | +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 |
| 183 | + |
| 184 | +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 |
| 185 | + |
0 commit comments