1579. 保证图可完全遍历

1579. 保证图可完全遍历

1579. 保证图可完全遍历

难度困难

Alice 和 Bob 共有一个无向图,其中包含 n 个节点和 3 种类型的边:

  • 类型 1:只能由 Alice 遍历。
  • 类型 2:只能由 Bob 遍历。
  • 类型 3:Alice 和 Bob 都可以遍历。

给你一个数组 edges ,其中 edges[i] = [typei, ui, vi] 表示节点 uivi 之间存在类型为 typei 的双向边。请你在保证图仍能够被 Alice和 Bob 完全遍历的前提下,找出可以删除的最大边数。如果从任何节点开始,Alice 和 Bob 都可以到达所有其他节点,则认为图是可以完全遍历的。

返回可以删除的最大边数,如果 Alice 和 Bob 无法完全遍历图,则返回 -1 。

示例 1:

img

输入: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:

img

输入:n = 4, edges = [[3,1,2],[3,2,3],[1,1,4],[2,1,4]]
输出:0
解释:注意,删除任何一条边都会使 Alice 和 Bob 无法完全遍历这个图。

示例 3:

img

输入: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) 互不相同

函数签名:

func maxNumEdgesToRemove(n int, edges [][]int) int

分析

尝试借助并查集解决。一开始将所有 n 个点看作各自为政的散点,根据 edges 来连接这些点。

按照题目诉求,要尽可能地少用边。首先可以确定,优先用类型为 3 即 Alice 和 Bob 都能通过的边较好。

先尽可能用 Alice 和 Bob 的共用边是对的:对于两个联通分量,如果要联通它们,可以用一条共用类型的边或者两条独用类型的边,显然用共用类型的边将省一条边。

为 Alice 和 Bob 分别创建一个并查集。先根据所有类型为 3 的边,对这两个并查集分别做一一合并,之后再分别针对独用边,对两个并查集做一一合并。

注意如果两个点已经联通则无需再合并,对应的边应该舍弃。

type UnionFind struct {
	parent []int
	set    int
}

func NewUnionFind(n int) *UnionFind {
	p := make([]int, n)
	for i := range p {
		p[i] = i
	}
	return &UnionFind{parent: p, set: n}
}
func (uf *UnionFind) Find(x int) int {
	for x != uf.parent[x] {
		x, uf.parent[x] = uf.parent[x], uf.parent[uf.parent[x]]
	}
	return uf.parent[x]
}
func (uf *UnionFind) Union(x, y int) bool {
	x, y = uf.Find(x), uf.Find(y)
	if x == y {
		return false
	}
	uf.parent[x] = y
	uf.set--
	return true
}
func maxNumEdgesToRemove(n int, edges [][]int) int {
	if n-1 > len(edges) {
		return -1
	}
	alice, bob := NewUnionFind(n), NewUnionFind(n)
	res := 0
	for _, e := range edges {
		if e[0] != 3 {
			continue
		}
		if alice.Union(e[1]-1, e[2]-1) {
			bob.Union(e[1]-1, e[2]-1)
		} else {
			res++
		}
	}
	for _, e := range edges {
		if e[0] == 1 && !alice.Union(e[1]-1, e[2]-1) ||
			e[0] == 2 && !bob.Union(e[1]-1, e[2]-1) {
			res++
		}
	}
	if alice.set != 1 || bob.set != 1 {
		return -1
	}
	return res
}