63
63
64
64
<!-- 这里可写通用的实现逻辑 -->
65
65
66
- 并查集。对于本题,构造两个并查集。同时操作两个并查集 ufa, ufb,遵从优先添加公共边的策略,即:优先添加 ` type = 3 ` 的边。添加的过程中,若两点已连通,则累加多余的边 ans。
66
+ ** 方法一:贪心 + 并查集 **
67
67
68
- 然后遍历 ` type = 1 ` 的边添加到 ufa 中,而 ` type = 2 ` 的边则添加到 ufb 中。此过程同样判断两点是否已连通,若是,则累加多余的边 ans 。
68
+ 题目要求我们删除最多数目的边,使得 Alice 和 Bob 都可以遍历整个图。也即是说,我们需要保留尽可能少的边,并且要求这些边能够使得 Alice 和 Bob 都可以遍历整个图 。
69
69
70
- 最后判断两个并查集是否都只有一个连通分量,若是,返回 ans,否则返回 -1 。
70
+ 我们可以用两个并查集 $ufa$ 和 $ufb$ 分别维护 Alice 和 Bob 的遍历情况 。
71
71
72
- 以下是并查集的几个常用模板 。
72
+ 接下来,我们优先遍历公共边,即 $type=3$ 的边。对于每一条公共边的两个端点 $u$ 和 $v$,如果这两个点已经在同一个连通分量中,那么我们就可以删去这条边,因此答案加一;否则我们就将这两个点合并,即执行 $ufa.union(u, v)$ 和 $ufb.union(u, v)$ 。
73
73
74
- 模板 1——朴素并查集:
74
+ 然后,我们再遍历 Alice 独有的边,即 $type=1$ 的边。对于每一条 Alice 独有的边的两个端点 $u$ 和 $v$,如果这两个点已经在同一个连通分量中,那么我们就可以删去这条边,答案加一;否则我们就将这两个点合并,即执行 $ufa.union(u, v)$。同理,对于 Bob 独有的边,我们也可以执行相同的操作。
75
75
76
- ``` python
77
- # 初始化,p存储每个点的父节点
78
- p = list (range (n))
79
- # 返回x的祖宗节点
80
- def find (x ):
81
- if p[x] != x:
82
- # 路径压缩
83
- p[x] = find(p[x])
84
- return p[x]
85
-
86
-
87
- # 合并a和b所在的两个集合
88
- p[find(a)] = find(b)
89
- ```
90
-
91
- 模板 2——维护 size 的并查集:
92
-
93
- ``` python
94
- # 初始化,p存储每个点的父节点,size只有当节点是祖宗节点时才有意义,表示祖宗节点所在集合中,点的数量
95
- p = list (range (n))
96
- size = [1 ] * n
97
- # 返回x的祖宗节点
98
- def find (x ):
99
- if p[x] != x:
100
- # 路径压缩
101
- p[x] = find(p[x])
102
- return p[x]
103
- # 合并a和b所在的两个集合
104
- if find(a) != find(b):
105
- size[find(b)] += size[find(a)]
106
- p[find(a)] = find(b)
107
- ```
76
+ 最后,如果 Alice 和 Bob 都可以遍历整个图,那么答案就是我们删除的边数;否则答案就是 $-1$。
108
77
109
- 模板 3——维护到祖宗节点距离的并查集:
110
-
111
- ``` python
112
- # 初始化,p存储每个点的父节点,d[x]存储x到p[x]的距离
113
- p = list (range (n))
114
- d = [0 ] * n
115
- # 返回x的祖宗节点
116
- def find (x ):
117
- if p[x] != x:
118
- t = find(p[x])
119
- d[x] += d[p[x]]
120
- p[x] = t
121
- return p[x]
122
- # 合并a和b所在的两个集合
123
- p[find(a)] = find(b)
124
- d[find(a)] = distance
125
- ```
78
+ 时间复杂度 $O(m \times \alpha(n))$,空间复杂度 $O(n)$。其中 $m$ 是边数,而 $\alpha(n)$ 是阿克曼函数的反函数。
126
79
127
80
<!-- tabs:start -->
128
81
@@ -134,93 +87,116 @@ d[find(a)] = distance
134
87
class UnionFind :
135
88
def __init__ (self , n ):
136
89
self .p = list (range (n))
137
- self .n = n
90
+ self .size = [1 ] * n
91
+ self .cnt = n
92
+
93
+ def find (self , x ):
94
+ if self .p[x] != x:
95
+ self .p[x] = self .find(self .p[x])
96
+ return self .p[x]
138
97
139
98
def union (self , a , b ):
140
99
pa, pb = self .find(a - 1 ), self .find(b - 1 )
141
100
if pa == pb:
142
101
return False
143
- self .p[pa] = pb
144
- self .n -= 1
102
+ if self .size[pa] > self .size[pb]:
103
+ self .p[pb] = pa
104
+ self .size[pa] += self .size[pb]
105
+ else :
106
+ self .p[pa] = pb
107
+ self .size[pb] += self .size[pa]
108
+ self .cnt -= 1
145
109
return True
146
110
147
- def find (self , x ):
148
- if self .p[x] != x:
149
- self .p[x] = self .find(self .p[x])
150
- return self .p[x]
151
-
152
111
153
112
class Solution :
154
113
def maxNumEdgesToRemove (self , n : int , edges : List[List[int ]]) -> int :
155
- ufa, ufb = UnionFind(n), UnionFind(n)
114
+ ufa = UnionFind(n)
115
+ ufb = UnionFind(n)
156
116
ans = 0
157
117
for t, u, v in edges:
158
118
if t == 3 :
159
119
if ufa.union(u, v):
160
120
ufb.union(u, v)
161
121
else :
162
122
ans += 1
163
- ans += sum ((t == 1 and not ufa.union(u, v))
164
- or (t == 2 and not ufb.union(u, v)) for t, u, v in edges)
165
- return ans if ufa.n == 1 and ufb.n == 1 else - 1
123
+ for t, u, v in edges:
124
+ if t == 1 :
125
+ ans += not ufa.union(u, v)
126
+ if t == 2 :
127
+ ans += not ufb.union(u, v)
128
+ return ans if ufa.cnt == 1 and ufb.cnt == 1 else - 1
166
129
```
167
130
168
131
### ** Java**
169
132
170
133
<!-- 这里可写当前语言的特殊实现逻辑 -->
171
134
172
135
``` java
173
- class Solution {
174
- public int maxNumEdgesToRemove (int n , int [][] edges ) {
175
- UnionFind ufa = new UnionFind (n);
176
- UnionFind ufb = new UnionFind (n);
177
- int ans = 0 ;
178
- for (int [] e : edges) {
179
- if (e[0 ] == 3 ) {
180
- if (ufa. union(e[1 ], e[2 ])) {
181
- ufb. union(e[1 ], e[2 ]);
182
- } else {
183
- ++ ans;
184
- }
185
- }
186
- }
187
- for (int [] e : edges) {
188
- if ((e[0 ] == 1 && ! ufa. union(e[1 ], e[2 ])) || (e[0 ] == 2 && ! ufb. union(e[1 ], e[2 ]))) {
189
- ++ ans;
190
- }
191
- }
192
- return ufa. n == 1 && ufb. n == 1 ? ans : - 1 ;
193
- }
194
- }
195
-
196
136
class UnionFind {
197
- public int [] p;
198
- public int n;
137
+ private int [] p;
138
+ private int [] size;
139
+ public int cnt;
199
140
200
141
public UnionFind (int n ) {
201
142
p = new int [n];
143
+ size = new int [n];
202
144
for (int i = 0 ; i < n; ++ i) {
203
145
p[i] = i;
146
+ size[i] = 1 ;
147
+ }
148
+ cnt = n;
149
+ }
150
+
151
+ public int find (int x ) {
152
+ if (p[x] != x) {
153
+ p[x] = find(p[x]);
204
154
}
205
- this . n = n ;
155
+ return p[x] ;
206
156
}
207
157
208
158
public boolean union (int a , int b ) {
209
- int pa = find(a - 1 );
210
- int pb = find(b - 1 );
159
+ int pa = find(a - 1 ), pb = find(b - 1 );
211
160
if (pa == pb) {
212
161
return false ;
213
162
}
214
- p[pa] = pb;
215
- -- n;
163
+ if (size[pa] > size[pb]) {
164
+ p[pb] = pa;
165
+ size[pa] += size[pb];
166
+ } else {
167
+ p[pa] = pb;
168
+ size[pb] += size[pa];
169
+ }
170
+ -- cnt;
216
171
return true ;
217
172
}
173
+ }
218
174
219
- public int find (int x ) {
220
- if (p[x] != x) {
221
- p[x] = find(p[x]);
175
+ class Solution {
176
+ public int maxNumEdgesToRemove (int n , int [][] edges ) {
177
+ UnionFind ufa = new UnionFind (n);
178
+ UnionFind ufb = new UnionFind (n);
179
+ int ans = 0 ;
180
+ for (var e : edges) {
181
+ int t = e[0 ], u = e[1 ], v = e[2 ];
182
+ if (t == 3 ) {
183
+ if (ufa. union(u, v)) {
184
+ ufb. union(u, v);
185
+ } else {
186
+ ++ ans;
187
+ }
188
+ }
222
189
}
223
- return p[x];
190
+ for (var e : edges) {
191
+ int t = e[0 ], u = e[1 ], v = e[2 ];
192
+ if (t == 1 && ! ufa. union(u, v)) {
193
+ ++ ans;
194
+ }
195
+ if (t == 2 && ! ufb. union(u, v)) {
196
+ ++ ans;
197
+ }
198
+ }
199
+ return ufa. cnt == 1 && ufb. cnt == 1 ? ans : - 1 ;
224
200
}
225
201
}
226
202
```
@@ -230,46 +206,64 @@ class UnionFind {
230
206
``` cpp
231
207
class UnionFind {
232
208
public:
233
- vector<int > p;
234
- int n;
209
+ int cnt;
235
210
236
- UnionFind(int _n)
237
- : n(_n)
238
- , p(_n) {
211
+ UnionFind(int n) {
212
+ p = vector<int>(n);
213
+ size = vector<int>(n, 1);
239
214
iota(p.begin(), p.end(), 0);
215
+ cnt = n;
240
216
}
241
217
242
218
bool unite (int a, int b) {
243
219
int pa = find(a - 1), pb = find(b - 1);
244
- if (pa == pb) return false;
245
- p[ pa] = pb;
246
- --n;
220
+ if (pa == pb) {
221
+ return false;
222
+ }
223
+ if (size[ pa] > size[ pb] ) {
224
+ p[ pb] = pa;
225
+ size[ pa] += size[ pb] ;
226
+ } else {
227
+ p[ pa] = pb;
228
+ size[ pb] += size[ pa] ;
229
+ }
230
+ --cnt;
247
231
return true;
248
232
}
249
233
250
234
int find(int x) {
251
- if (p[x] != x) p[x] = find(p[x]);
235
+ if (p[x] != x) {
236
+ p[x] = find(p[x]);
237
+ }
252
238
return p[x];
253
239
}
240
+
241
+ private:
242
+ vector<int > p, size;
254
243
};
255
244
256
245
class Solution {
257
246
public:
258
247
int maxNumEdgesToRemove(int n, vector<vector<int >>& edges) {
259
- UnionFind ufa(n), ufb(n);
248
+ UnionFind ufa(n);
249
+ UnionFind ufb(n);
260
250
int ans = 0;
261
251
for (auto& e : edges) {
262
- if (e[ 0] == 3) {
263
- if (ufa.unite(e[ 1] , e[ 2] ))
264
- ufb.unite(e[ 1] , e[ 2] );
265
- else
252
+ int t = e[ 0] , u = e[ 1] , v = e[ 2] ;
253
+ if (t == 3) {
254
+ if (ufa.unite(u, v)) {
255
+ ufb.unite(u, v);
256
+ } else {
266
257
++ans;
258
+ }
267
259
}
268
260
}
269
- for (auto& e : edges)
270
- if ((e[ 0] == 1 && !ufa.unite(e[ 1] , e[ 2] )) || (e[ 0] == 2 && !ufb.unite(e[ 1] , e[ 2] )))
271
- ++ans;
272
- return ufa.n == 1 && ufb.n == 1 ? ans : -1;
261
+ for (auto& e : edges) {
262
+ int t = e[ 0] , u = e[ 1] , v = e[ 2] ;
263
+ ans += t == 1 && !ufa.unite(u, v);
264
+ ans += t == 2 && !ufb.unite(u, v);
265
+ }
266
+ return ufa.cnt == 1 && ufb.cnt == 1 ? ans : -1;
273
267
}
274
268
};
275
269
```
@@ -278,16 +272,18 @@ public:
278
272
279
273
```go
280
274
type unionFind struct {
281
- p []int
282
- n int
275
+ p, size []int
276
+ cnt int
283
277
}
284
278
285
279
func newUnionFind(n int) *unionFind {
286
280
p := make([]int, n)
281
+ size := make([]int, n)
287
282
for i := range p {
288
283
p[i] = i
284
+ size[i] = 1
289
285
}
290
- return &unionFind{p, n}
286
+ return &unionFind{p, size, n}
291
287
}
292
288
293
289
func (uf *unionFind) find(x int) int {
@@ -302,30 +298,41 @@ func (uf *unionFind) union(a, b int) bool {
302
298
if pa == pb {
303
299
return false
304
300
}
305
- uf.p[pa] = pb
306
- uf.n--
301
+ if uf.size[pa] > uf.size[pb] {
302
+ uf.p[pb] = pa
303
+ uf.size[pa] += uf.size[pb]
304
+ } else {
305
+ uf.p[pa] = pb
306
+ uf.size[pb] += uf.size[pa]
307
+ }
308
+ uf.cnt--
307
309
return true
308
310
}
309
311
310
- func maxNumEdgesToRemove(n int, edges [][]int) int {
311
- ufa, ufb := newUnionFind(n), newUnionFind(n)
312
- ans := 0
312
+ func maxNumEdgesToRemove(n int, edges [][]int) (ans int) {
313
+ ufa := newUnionFind(n)
314
+ ufb := newUnionFind(n)
313
315
for _, e := range edges {
314
- if e[0] == 3 {
315
- if ufa.union(e[1], e[2]) {
316
- ufb.union(e[1], e[2])
316
+ t, u, v := e[0], e[1], e[2]
317
+ if t == 3 {
318
+ if ufa.union(u, v) {
319
+ ufb.union(u, v)
317
320
} else {
318
321
ans++
319
322
}
320
323
}
321
324
}
322
325
for _, e := range edges {
323
- if (e[0] == 1 && !ufa.union(e[1], e[2])) || (e[0] == 2 && !ufb.union(e[1], e[2])) {
326
+ t, u, v := e[0], e[1], e[2]
327
+ if t == 1 && !ufa.union(u, v) {
328
+ ans++
329
+ }
330
+ if t == 2 && !ufb.union(u, v) {
324
331
ans++
325
332
}
326
333
}
327
- if ufa.n == 1 && ufb.n == 1 {
328
- return ans
334
+ if ufa.cnt == 1 && ufb.cnt == 1 {
335
+ return
329
336
}
330
337
return -1
331
338
}
0 commit comments