题目描述
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
。
样例
输入: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 。
输入:n = 4, edges = [[3,1,2],[3,2,3],[1,1,4],[2,1,4]]
输出:0
解释:注意,删除任何一条边都会使 Alice 和 Bob 无法完全遍历这个图。
输入: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)
互不相同
算法分析
kruskal
算法的拓展
- 1、删除最多的的边使得
Alice
和Bob
可以完全遍历,即等价于使用最少的边能让Alice
和Bob
完全遍历整个图 - 2、有
n
个点,能让Alice
和Bob
完全遍历整个图需要最多的边是2(n - 1)
,让公共边尽可能连接多的点,能让Alice
和Bob
同时使用的公共边越多,从而能够使得使用的总边数最少 - 3、通过并查集的方式使用公共边让尽可能多的点连接在一起,并记录当前使用过的公共边的边数
cnt
Alice
:通过并查集的方式将其他点使用Alice
独特的边全部连接形成一棵树,并记录使用了Alice
的边数cntA
Bob
:通过并查集的方式将其他点使用Bob
独特的边全部连接形成一棵树,并记录使用了Bob
的边数cntB
- 4、判断
Alice
是否能完全遍历,即判断cnt + cntA == n - 1
的情况,若满足则遍历可以将所有点连接在一起,若不满足直接返回-1
;判断Bob
的同理 - 5、返回最大删除的边数
m - (cnt + cntA + cntB)
即可
公共边的连接情况
公共边 + Alice独特边的连接情况
公共边 + Bob独特边的连接情况
时间复杂度 $O(m)$
m是边数
Java 代码
class Solution {
static int find(int x, int[] p)
{
if(p[x] != x) p[x] = find(p[x], p);
return p[x];
}
public int maxNumEdgesToRemove(int n, int[][] edges) {
int m = edges.length;
int[] p = new int[n + 1];
for(int i = 1;i <= n;i ++) p[i] = i;
int cnt = 0;//公共边能连通点的边数
for(int i = 0;i < m;i ++)
{
int type = edges[i][0];
int a = edges[i][1];
int b = edges[i][2];
if(type == 3)
{
a = find(a, p); b = find(b, p);
if(a != b)
{
p[a] = b;
cnt ++;
}
}
}
//复制p的状态两份
int[] pa = new int[n + 1];
int[] pb = new int[n + 1];
for(int i = 0;i <= n;i ++) pa[i] = pb[i] = p[i];
int cntA = 0, cntB = 0;
for(int i = 0;i < m;i ++)
{
int type = edges[i][0];
int a = edges[i][1];
int b = edges[i][2];
if(type == 1)
{
a = find(a, pa); b = find(b, pa);
if(a != b)
{
pa[a] = b;
cntA ++;
}
}
if(type == 2)
{
a = find(a, pb); b = find(b, pb);
if(a != b)
{
pb[a] = b;
cntB ++;
}
}
}
if(cnt + cntA != n - 1 || cnt + cntB != n - 1) return -1;
return m - (cnt + cntA + cntB);
}
}
强