Skip to content

Commit 7d763e8

Browse files
committed
feat: add solutions to lc problem: No.1579
No.1579.Remove Max Number of Edges to Keep Graph Fully Traversable
1 parent c68ad0e commit 7d763e8

File tree

6 files changed

+403
-292
lines changed

6 files changed

+403
-292
lines changed

solution/1500-1599/1579.Remove Max Number of Edges to Keep Graph Fully Traversable/README.md

Lines changed: 139 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -63,66 +63,19 @@
6363

6464
<!-- 这里可写通用的实现逻辑 -->
6565

66-
并查集。对于本题,构造两个并查集。同时操作两个并查集 ufa, ufb,遵从优先添加公共边的策略,即:优先添加 `type = 3` 的边。添加的过程中,若两点已连通,则累加多余的边 ans。
66+
**方法一:贪心 + 并查集**
6767

68-
然后遍历 `type = 1` 的边添加到 ufa 中,而 `type = 2` 的边则添加到 ufb 中。此过程同样判断两点是否已连通,若是,则累加多余的边 ans
68+
题目要求我们删除最多数目的边,使得 Alice 和 Bob 都可以遍历整个图。也即是说,我们需要保留尽可能少的边,并且要求这些边能够使得 Alice 和 Bob 都可以遍历整个图
6969

70-
最后判断两个并查集是否都只有一个连通分量,若是,返回 ans,否则返回 -1
70+
我们可以用两个并查集 $ufa$ 和 $ufb$ 分别维护 Alice 和 Bob 的遍历情况
7171

72-
以下是并查集的几个常用模板
72+
接下来,我们优先遍历公共边,即 $type=3$ 的边。对于每一条公共边的两个端点 $u$ 和 $v$,如果这两个点已经在同一个连通分量中,那么我们就可以删去这条边,因此答案加一;否则我们就将这两个点合并,即执行 $ufa.union(u, v)$ 和 $ufb.union(u, v)$
7373

74-
模板 1——朴素并查集:
74+
然后,我们再遍历 Alice 独有的边,即 $type=1$ 的边。对于每一条 Alice 独有的边的两个端点 $u$ 和 $v$,如果这两个点已经在同一个连通分量中,那么我们就可以删去这条边,答案加一;否则我们就将这两个点合并,即执行 $ufa.union(u, v)$。同理,对于 Bob 独有的边,我们也可以执行相同的操作。
7575

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$。
10877

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)$ 是阿克曼函数的反函数。
12679

12780
<!-- tabs:start -->
12881

@@ -134,93 +87,116 @@ d[find(a)] = distance
13487
class UnionFind:
13588
def __init__(self, n):
13689
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]
13897

13998
def union(self, a, b):
14099
pa, pb = self.find(a - 1), self.find(b - 1)
141100
if pa == pb:
142101
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
145109
return True
146110

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-
152111

153112
class Solution:
154113
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)
156116
ans = 0
157117
for t, u, v in edges:
158118
if t == 3:
159119
if ufa.union(u, v):
160120
ufb.union(u, v)
161121
else:
162122
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
166129
```
167130

168131
### **Java**
169132

170133
<!-- 这里可写当前语言的特殊实现逻辑 -->
171134

172135
```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-
196136
class UnionFind {
197-
public int[] p;
198-
public int n;
137+
private int[] p;
138+
private int[] size;
139+
public int cnt;
199140

200141
public UnionFind(int n) {
201142
p = new int[n];
143+
size = new int[n];
202144
for (int i = 0; i < n; ++i) {
203145
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]);
204154
}
205-
this.n = n;
155+
return p[x];
206156
}
207157

208158
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);
211160
if (pa == pb) {
212161
return false;
213162
}
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;
216171
return true;
217172
}
173+
}
218174

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+
}
222189
}
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;
224200
}
225201
}
226202
```
@@ -230,46 +206,64 @@ class UnionFind {
230206
```cpp
231207
class UnionFind {
232208
public:
233-
vector<int> p;
234-
int n;
209+
int cnt;
235210

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);
239214
iota(p.begin(), p.end(), 0);
215+
cnt = n;
240216
}
241217

242218
bool unite(int a, int b) {
243219
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;
247231
return true;
248232
}
249233

250234
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+
}
252238
return p[x];
253239
}
240+
241+
private:
242+
vector<int> p, size;
254243
};
255244

256245
class Solution {
257246
public:
258247
int maxNumEdgesToRemove(int n, vector<vector<int>>& edges) {
259-
UnionFind ufa(n), ufb(n);
248+
UnionFind ufa(n);
249+
UnionFind ufb(n);
260250
int ans = 0;
261251
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 {
266257
++ans;
258+
}
267259
}
268260
}
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;
273267
}
274268
};
275269
```
@@ -278,16 +272,18 @@ public:
278272
279273
```go
280274
type unionFind struct {
281-
p []int
282-
n int
275+
p, size []int
276+
cnt int
283277
}
284278
285279
func newUnionFind(n int) *unionFind {
286280
p := make([]int, n)
281+
size := make([]int, n)
287282
for i := range p {
288283
p[i] = i
284+
size[i] = 1
289285
}
290-
return &unionFind{p, n}
286+
return &unionFind{p, size, n}
291287
}
292288
293289
func (uf *unionFind) find(x int) int {
@@ -302,30 +298,41 @@ func (uf *unionFind) union(a, b int) bool {
302298
if pa == pb {
303299
return false
304300
}
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--
307309
return true
308310
}
309311
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)
313315
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)
317320
} else {
318321
ans++
319322
}
320323
}
321324
}
322325
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) {
324331
ans++
325332
}
326333
}
327-
if ufa.n == 1 && ufb.n == 1 {
328-
return ans
334+
if ufa.cnt == 1 && ufb.cnt == 1 {
335+
return
329336
}
330337
return -1
331338
}

0 commit comments

Comments
 (0)