难度困难58
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)
互不相同
优先使用都允许通过的边,之后分别对每个类型的边进行最小全集边数的确定,中间需要保存中间结果。
class Solution {
//并查集(union-find sets)
int Par[100001]; //父节点
int Rank[100001]; //树深度
int Par_save[100001]; //父节点存储
int Rank_save[100001]; //树深度存储
int num = 0;
void Make_Set(int N)
{
for (int i = 0; i < N; i++)
{
Par[i] = i;
Rank[i] = 0;
}
}
void Set_save(int n) {
for (int i = 0; i < n; i++)
{
Par_save[i] = Par[i];
Rank_save[i] = Rank[i];
}
}
void Set_load(int n) {
for (int i = 0; i < n; i++)
{
Par[i] = Par_save[i];
Rank[i] = Rank_save[i];
}
}
int Find_Set(int x)
{
if (Par[x] == x)
return x;
else
return Find_Set(Par[x]);
}
int Union(int x, int y)
{
//static int num = 0;
x = Find_Set(x);
y = Find_Set(y);
if (x == y) //已经在一个集合了
return 0;
num++; //已经使用的边数
if (Rank[x] < Rank[y])
Par[x] = y;
else
{
Par[y] = x;
if (Rank[x] == Rank[y])
Rank[x]++;
}
return num;
}
public:
int maxNumEdgesToRemove(int n, vector<vector<int>>& edges) {
int k = 0;
int cost = 0;
Make_Set(n);
for (int i = 0; i < edges.size(); ++i) {
if (edges[i][0] == 3) {
k = Union(edges[i][1], edges[i][2]);
if (k != 0)
{
cost++;
if (k == n - 1) //所有节点已全部遍历
{
return edges.size() - cost;
}
}
}
}
int save = n - cost - 1;
Set_save(n);
for (int i = 0; i < edges.size(); ++i) {
if (edges[i][0] == 2) {
k = Union(edges[i][1], edges[i][2]);
if (k != 0)
{
cost++;
if (k == n - 1) //所有节点已全部遍历
break;
}
}
}
if (k != n - 1)
return -1;
Set_load(n);
for (int i = 0; i < edges.size(); ++i) {
if (edges[i][0] == 1) {
k = Union(edges[i][1], edges[i][2]);
if (k != 0)
{
cost++;
if (k == n - 1 + save) //所有节点已全部遍历
break;
}
}
}
if (k != n - 1 + save)
return -1;
else
return edges.size() - cost;
}
};