leetcode 6256. 将节点分成尽可能多的组 二分图判定+bfs+并查集
6256. 将节点分成尽可能多的组
难度困难
给你一个正整数 n
,表示一个 无向 图中的节点数目,节点编号从 1
到 n
。
同时给你一个二维整数数组 edges
,其中 edges[i] = [ai, bi]
表示节点 ai
和 bi
之间有一条 双向 边。注意给定的图可能是不连通的。
请你将图划分为 m
个组(编号从 1 开始),满足以下要求:
- 图中每个节点都只属于一个组。
- 图中每条边连接的两个点
[ai, bi]
,如果ai
属于编号为x
的组,bi
属于编号为y
的组,那么|y - x| = 1
。
请你返回最多可以将节点分为多少个组(也就是最大的 m
)。如果没办法在给定条件下分组,请你返回 -1
。
示例 1:
输入:n = 6, edges = [[1,2],[1,4],[1,5],[2,6],[2,3],[4,6]] 输出:4 解释:如上图所示, - 节点 5 在第一个组。 - 节点 1 在第二个组。 - 节点 2 和节点 4 在第三个组。 - 节点 3 和节点 6 在第四个组。 所有边都满足题目要求。 如果我们创建第五个组,将第三个组或者第四个组中任何一个节点放到第五个组,至少有一条边连接的两个节点所属的组编号不符合题目要求。
示例 2:
输入:n = 3, edges = [[1,2],[2,3],[3,1]] 输出:-1 解释:如果我们将节点 1 放入第一个组,节点 2 放入第二个组,节点 3 放入第三个组,前两条边满足题目要求,但第三条边不满足题目要求。 没有任何符合题目要求的分组方式。
提示:
1 <= n <= 500
1 <= edges.length <= 104
edges[i].length == 2
1 <= ai, bi <= n
ai != bi
- 两个点之间至多只有一条边
解法一
class Solution: def magnificentSets(self, n: int, edges: List[List[int]]) -> int: adjList = [[] for _ in range(n)] uf = UnionFind(n) for u, v in edges: u, v = u - 1, v - 1 adjList[u].append(v) adjList[v].append(u) uf.union(u, v) if not isBipartite(n, adjList): return -1 return sum(calDiameter(n, adjList, group) + 1 for group in uf.getGroups().values()) def calDiameter(n: int, adjList: List[List[int]], group: List[int]) -> int: """bfs求连通分量 `group` 的直径长度""" res = 0 for start in group: visited, queue = set([start]), deque([start]) diameter = -1 while queue: len_ = len(queue) for _ in range(len_): cur = queue.popleft() for next in adjList[cur]: if next in visited: continue visited.add(next) queue.append(next) diameter += 1 res = max(res, diameter) return res def isBipartite(n: int, adjList: List[List[int]]) -> bool: """二分图检测 dfs染色""" def dfs(cur: int, color: int) -> bool: colors[cur] = color for next in adjList[cur]: if colors[next] == -1: if not dfs(next, color ^ 1): return False elif colors[next] == color: return False return True colors = [-1] * n for i in range(n): if colors[i] == -1 and not dfs(i, 0): return False return True class UnionFind: def __init__(self, n: int): self.n = n self.part = n self.parent = list(range(n)) self.rank = [1] * n def find(self, x: int) -> int: while x != self.parent[x]: self.parent[x] = self.parent[self.parent[x]] x = self.parent[x] return x def union(self, x: int, y: int) -> bool: rootX = self.find(x) rootY = self.find(y) if rootX == rootY: return False if self.rank[rootX] > self.rank[rootY]: rootX, rootY = rootY, rootX self.parent[rootX] = rootY self.rank[rootY] += self.rank[rootX] self.part -= 1 return True def getGroups(self) -> DefaultDict[int, List[int]]: groups = defaultdict(list) for key in range(self.n): root = self.find(key) groups[root].append(key) return groups
解法二 # class Solution: # def magnificentSets(self, n: int, edges: List[List[int]]) -> int: # g = [[] for _ in range(n)] # for x, y in edges: # g[x - 1].append(y -1) # g[y - 1].append(x - 1) # time = [0] * n # clock = 0 # def bfs(start): # depth = 0 # nonlocal clock # clock += 1 # time[start] = clock # q = [start] # while q: # tmp = q # q = [] # for x in tmp: # for y in g[x]: # if time[y] != clock: # time[y] = clock # q.append(y) # depth += 1 # return depth # color = [0] * n # def is_bipartite(x, c): # nodes.append(x) # color[x] = c # for y in g[x]: # if color[y] == c or color[y] == 0 and not is_bipartite(y, -c): # return False # return True # ans = 0 # for i, c in enumerate(color): # if c: continue # nodes = [] # print (color) # t = is_bipartite(i, 1) # print (color) # if not t: return -1 # ans += max(bfs(x) for x in nodes) # print (ans) # return ans