-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathremove-max-number-of-edges-to-keep-graph-fully-traversable.py
113 lines (103 loc) · 3.65 KB
/
remove-max-number-of-edges-to-keep-graph-fully-traversable.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# 1579. Remove Max Number of Edges to Keep Graph Fully Traversable
# 🔴 Hard
#
# https://leetcode.com/problems/remove-max-number-of-edges-to-keep-graph-fully-traversable/
#
# Tags: Union Find - Graph
import timeit
from typing import List
# We can use two DSU structures, one for Alice and one for Bob, we only
# add the edges that connect previously unconnected components and then
# check how many of them we used and how many we did not use.
#
# Time complexity: O(max(n, e)) - We iterate over the edges twice, we
# iterate over n elements to create the auxiliary structures.
# Space complexity: O(n) - The parents and ranks arrays have size n.
#
# Runtime 2184 ms Beats 63.13%
# Memory 57 MB Beats 18.13%
class Solution:
def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int:
# The number of disjoint sets in both DSU.
cb = n
parents_bob = [x for x in range(n)]
# parents_alice = [i for i in range(n + 1)]
rank_bob = [1] * n
# rank_alice = [1] * (n + 1)
# Find the parent of the given node.
def find(a: int, is_bob: bool) -> int:
parents = parents_bob if is_bob else parents_alice
if parents[a] != a:
# Path compression.
parents[a] = find(parents[a], is_bob)
return parents[a]
# Union by rank. Returns false if the nodes are already
# connected.
def union(a: int, b: int, is_bob: bool) -> bool:
parents = parents_bob if is_bob else parents_alice
rank = rank_bob if is_bob else rank_alice
# Find the parents.
pa, pb = find(a, is_bob), find(b, is_bob)
if pa == pb:
return False
# If pb has a higher rank, make it the parent.
if rank[pb] > rank[pa]:
pa, pb = pb, pa
parents[pb] = pa
rank[pa] += rank[pb]
return True
# Store the number of edges we use.
res = 0
# Create the disjoint sets. First only type 3 edges.
for t, a, b in edges:
if t == 3:
if union(a - 1, b - 1, True):
cb -= 1
else:
res += 1
parents_alice = parents_bob[::]
rank_alice = rank_bob[::]
ca = cb
for t, a, b in edges:
a, b = a - 1, b - 1
if t == 2:
if union(a, b, True):
cb -= 1
else:
res += 1
if t == 1:
if union(a, b, False):
ca -= 1
else:
res += 1
if max(ca, cb) > 1:
return -1
return res
def test():
executors = [Solution]
tests = [
[4, [[3, 2, 3], [1, 1, 2], [2, 3, 4]], -1],
[4, [[3, 1, 2], [3, 2, 3], [1, 1, 4], [2, 1, 4]], 0],
[
4,
[[3, 1, 2], [3, 2, 3], [1, 1, 3], [1, 2, 4], [1, 1, 2], [2, 3, 4]],
2,
],
]
for executor in executors:
start = timeit.default_timer()
for _ in range(1):
for col, t in enumerate(tests):
sol = executor()
result = sol.maxNumEdgesToRemove(t[0], t[1])
exp = t[2]
assert result == exp, (
f"\033[93m» {result} <> {exp}\033[91m for"
+ f" test {col} using \033[1m{executor.__name__}"
)
stop = timeit.default_timer()
used = str(round(stop - start, 5))
cols = "{0:20}{1:10}{2:10}"
res = cols.format(executor.__name__, used, "seconds")
print(f"\033[92m» {res}\033[0m")
test()