并查集
图
题目描述
Alice 和 Bob 共有一个无向图,其中包含 n 个节点和 3 种类型的边:
类型 1:只能由 Alice 遍历。
类型 2:只能由 Bob 遍历。
类型 3:Alice 和 Bob 都可以遍历。
给你一个数组 edges
,其中 edges[i] = [typei , ui , vi ]
表示节点 ui
和 vi
之间存在类型为 typei
的双向边。请你在保证图仍能够被 Alice和 Bob 完全遍历的前提下,找出可以删除的最大边数。如果从任何节点开始,Alice 和 Bob 都可以到达所有其他节点,则认为图是可以完全遍历的。
返回可以删除的最大边数,如果 Alice 和 Bob 无法完全遍历图,则返回 -1 。
示例 1:
输入: n = 4, edges = [[3,1,2],[3,2,3],[1,1,3],[1,2,4],[1,1,2],[2,3,4]]
输出: 2
解释: 如果删除 [1,1,2] 和 [1,1,3] 这两条边,Alice 和 Bob 仍然可以完全遍历这个图。再删除任何其他的边都无法保证图可以完全遍历。所以可以删除的最大边数是 2 。
示例 2:
输入: n = 4, edges = [[3,1,2],[3,2,3],[1,1,4],[2,1,4]]
输出: 0
解释: 注意,删除任何一条边都会使 Alice 和 Bob 无法完全遍历这个图。
示例 3:
输入: n = 4, edges = [[3,2,3],[1,1,2],[2,3,4]]
输出: -1
解释: 在当前图中,Alice 无法从其他节点到达节点 4 。类似地,Bob 也不能达到节点 1 。因此,图无法完全遍历。
提示:
1 <= n <= 10^5
1 <= edges.length <= min(10^5, 3 * n * (n-1) / 2)
edges[i].length == 3
1 <= edges[i][0] <= 3
1 <= edges[i][1] < edges[i][2] <= n
所有元组 (typei , ui , vi )
互不相同
解法
方法一:贪心 + 并查集
题目要求我们删除最多数目的边,使得 Alice 和 Bob 都可以遍历整个图。也即是说,我们需要保留尽可能少的边,并且要求这些边能够使得 Alice 和 Bob 都可以遍历整个图。
我们可以用两个并查集 $ufa$ 和 $ufb$ 分别维护 Alice 和 Bob 的遍历情况。
接下来,我们优先遍历公共边,即 $type=3$ 的边。对于每一条公共边的两个端点 $u$ 和 $v$,如果这两个点已经在同一个连通分量中,那么我们就可以删去这条边,因此答案加一;否则我们就将这两个点合并,即执行 $ufa.union(u, v)$ 和 $ufb.union(u, v)$。
然后,我们再遍历 Alice 独有的边,即 $type=1$ 的边。对于每一条 Alice 独有的边的两个端点 $u$ 和 $v$,如果这两个点已经在同一个连通分量中,那么我们就可以删去这条边,答案加一;否则我们就将这两个点合并,即执行 $ufa.union(u, v)$。同理,对于 Bob 独有的边,我们也可以执行相同的操作。
最后,如果 Alice 和 Bob 都可以遍历整个图,那么答案就是我们删除的边数;否则答案就是 $-1$。
时间复杂度 $O(m \times \alpha(n))$,空间复杂度 $O(n)$。其中 $m$ 是边数,而 $\alpha(n)$ 是阿克曼函数的反函数。
Python3 Java C++ Go
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 class UnionFind :
def __init__ ( self , n ):
self . p = list ( range ( n ))
self . size = [ 1 ] * n
self . cnt = n
def find ( self , x ):
if self . p [ x ] != x :
self . p [ x ] = self . find ( self . p [ x ])
return self . p [ x ]
def union ( self , a , b ):
pa , pb = self . find ( a - 1 ), self . find ( b - 1 )
if pa == pb :
return False
if self . size [ pa ] > self . size [ pb ]:
self . p [ pb ] = pa
self . size [ pa ] += self . size [ pb ]
else :
self . p [ pa ] = pb
self . size [ pb ] += self . size [ pa ]
self . cnt -= 1
return True
class Solution :
def maxNumEdgesToRemove ( self , n : int , edges : List [ List [ int ]]) -> int :
ufa = UnionFind ( n )
ufb = UnionFind ( n )
ans = 0
for t , u , v in edges :
if t == 3 :
if ufa . union ( u , v ):
ufb . union ( u , v )
else :
ans += 1
for t , u , v in edges :
if t == 1 :
ans += not ufa . union ( u , v )
if t == 2 :
ans += not ufb . union ( u , v )
return ans if ufa . cnt == 1 and ufb . cnt == 1 else - 1
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 class UnionFind {
private int [] p ;
private int [] size ;
public int cnt ;
public UnionFind ( int n ) {
p = new int [ n ] ;
size = new int [ n ] ;
for ( int i = 0 ; i < n ; ++ i ) {
p [ i ] = i ;
size [ i ] = 1 ;
}
cnt = n ;
}
public int find ( int x ) {
if ( p [ x ] != x ) {
p [ x ] = find ( p [ x ] );
}
return p [ x ] ;
}
public boolean union ( int a , int b ) {
int pa = find ( a - 1 ), pb = find ( b - 1 );
if ( pa == pb ) {
return false ;
}
if ( size [ pa ] > size [ pb ] ) {
p [ pb ] = pa ;
size [ pa ] += size [ pb ] ;
} else {
p [ pa ] = pb ;
size [ pb ] += size [ pa ] ;
}
-- cnt ;
return true ;
}
}
class Solution {
public int maxNumEdgesToRemove ( int n , int [][] edges ) {
UnionFind ufa = new UnionFind ( n );
UnionFind ufb = new UnionFind ( n );
int ans = 0 ;
for ( var e : edges ) {
int t = e [ 0 ] , u = e [ 1 ] , v = e [ 2 ] ;
if ( t == 3 ) {
if ( ufa . union ( u , v )) {
ufb . union ( u , v );
} else {
++ ans ;
}
}
}
for ( var e : edges ) {
int t = e [ 0 ] , u = e [ 1 ] , v = e [ 2 ] ;
if ( t == 1 && ! ufa . union ( u , v )) {
++ ans ;
}
if ( t == 2 && ! ufb . union ( u , v )) {
++ ans ;
}
}
return ufa . cnt == 1 && ufb . cnt == 1 ? ans : - 1 ;
}
}
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 class UnionFind {
public :
int cnt ;
UnionFind ( int n ) {
p = vector < int > ( n );
size = vector < int > ( n , 1 );
iota ( p . begin (), p . end (), 0 );
cnt = n ;
}
bool unite ( int a , int b ) {
int pa = find ( a - 1 ), pb = find ( b - 1 );
if ( pa == pb ) {
return false ;
}
if ( size [ pa ] > size [ pb ]) {
p [ pb ] = pa ;
size [ pa ] += size [ pb ];
} else {
p [ pa ] = pb ;
size [ pb ] += size [ pa ];
}
-- cnt ;
return true ;
}
int find ( int x ) {
if ( p [ x ] != x ) {
p [ x ] = find ( p [ x ]);
}
return p [ x ];
}
private :
vector < int > p , size ;
};
class Solution {
public :
int maxNumEdgesToRemove ( int n , vector < vector < int >>& edges ) {
UnionFind ufa ( n );
UnionFind ufb ( n );
int ans = 0 ;
for ( auto & e : edges ) {
int t = e [ 0 ], u = e [ 1 ], v = e [ 2 ];
if ( t == 3 ) {
if ( ufa . unite ( u , v )) {
ufb . unite ( u , v );
} else {
++ ans ;
}
}
}
for ( auto & e : edges ) {
int t = e [ 0 ], u = e [ 1 ], v = e [ 2 ];
ans += t == 1 && ! ufa . unite ( u , v );
ans += t == 2 && ! ufb . unite ( u , v );
}
return ufa . cnt == 1 && ufb . cnt == 1 ? ans : -1 ;
}
};
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 type unionFind struct {
p , size [] int
cnt int
}
func newUnionFind ( n int ) * unionFind {
p := make ([] int , n )
size := make ([] int , n )
for i := range p {
p [ i ] = i
size [ i ] = 1
}
return & unionFind { p , size , n }
}
func ( uf * unionFind ) find ( x int ) int {
if uf . p [ x ] != x {
uf . p [ x ] = uf . find ( uf . p [ x ])
}
return uf . p [ x ]
}
func ( uf * unionFind ) union ( a , b int ) bool {
pa , pb := uf . find ( a - 1 ), uf . find ( b - 1 )
if pa == pb {
return false
}
if uf . size [ pa ] > uf . size [ pb ] {
uf . p [ pb ] = pa
uf . size [ pa ] += uf . size [ pb ]
} else {
uf . p [ pa ] = pb
uf . size [ pb ] += uf . size [ pa ]
}
uf . cnt --
return true
}
func maxNumEdgesToRemove ( n int , edges [][] int ) ( ans int ) {
ufa := newUnionFind ( n )
ufb := newUnionFind ( n )
for _ , e := range edges {
t , u , v := e [ 0 ], e [ 1 ], e [ 2 ]
if t == 3 {
if ufa . union ( u , v ) {
ufb . union ( u , v )
} else {
ans ++
}
}
}
for _ , e := range edges {
t , u , v := e [ 0 ], e [ 1 ], e [ 2 ]
if t == 1 && ! ufa . union ( u , v ) {
ans ++
}
if t == 2 && ! ufb . union ( u , v ) {
ans ++
}
}
if ufa . cnt == 1 && ufb . cnt == 1 {
return
}
return - 1
}
GitHub