用以太网线缆将 n
台计算机连接成一个网络,计算机的编号从 0
到 n-1
。线缆用 connections
表示,其中 connections[i] = [a, b]
连接了计算机 a
和 b
。
网络中的任何一台计算机都可以通过网络直接或者间接访问同一个网络中其他任意一台计算机。
给你这个计算机网络的初始布线 connections
,你可以拔开任意两台直连计算机之间的线缆,并用它连接一对未直连的计算机。请你计算并返回使所有计算机都连通所需的最少操作次数。如果不可能,则返回 -1 。
输入格式:
第 1 行输入 n 和 m,分别表示计算机的个数和线缆个数,用空格分隔。接下来的 m 行输入,表示有线缆连接的计算机 a 和 b,用空格分隔。
输出格式:
对每一组输入,在一行中输出使所有计算机都连通所需的最少操作次数,如果不可能,则返回-1。
输入样例1:
如图所示:
这里相应地给出一组输入:
输入样例1:
4 3
0 1
0 2
1 2
输出样例1:
在这里给出相应的输出。例如:
1
解释:拔下计算机 1 和 2 之间的线缆,并将它插到计算机 1 和 3 上。
输入样例2:
6 4
0 1
0 2
0 3
1 2
输出样例2:
在这里给出相应的输出。例如:
-1
解释:线缆数量不足。
提示
-
1 <= n <= 10^5
-
1 <= connections.length <= min(n*(n-1)/2, 10^5)
-
connections[i].length == 2
-
0 <= connections[i][0], connections[i][1] < n
-
connections[i][0] != connections[i][1]
-
没有重复的连接。
-
两台计算机不会通过多条线缆连接。
class UnionFind:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [1] * n
self.count = n # 连通分量的数量
def find(self, p):
if self.parent[p] != p:
self.parent[p] = self.find(self.parent[p]) # 路径压缩
return self.parent[p]
def union(self, p, q):
rootP = self.find(p)
rootQ = self.find(q)
if rootP != rootQ:
if self.rank[rootP] > self.rank[rootQ]:
self.parent[rootQ] = rootP
elif self.rank[rootP] < self.rank[rootQ]:
self.parent[rootP] = rootQ
else:
self.parent[rootQ] = rootP
self.rank[rootP] += 1
self.count -= 1 # 每合并一次,连通分量数量减1
def make_connected(n, connections):
if len(connections) < n - 1:
return -1 # 线缆数量不足
uf = UnionFind(n)
for a, b in connections:
uf.union(a, b)
return uf.count - 1 # 需要的操作次数是连通分量数量减1
# 读取输入
n, m = map(int, input().strip().split())
connections = [list(map(int, input().strip().split())) for _ in range(m)]
# 计算并输出结果
print(make_connected(n, connections))
代码解释
并查集类:UnionFind 类实现了并查集的数据结构,包含路径压缩和按秩合并的优化。
__init__ 方法初始化父节点数组 parent、秩数组 rank 和连通分量数量 count。
find 方法实现路径压缩,找到节点的根节点。
union 方法实现按秩合并,并更新连通分量数量。
主函数:make_connected 函数实现了主要的逻辑。
首先检查线缆数量是否足够。
然后使用并查集处理所有连接,统计连通分量数量。
最后返回需要的最少操作次数。