问题描述
小 M 有n个点,每个点的坐标为 (, )。她可以从一个点出发,沿着坐标轴方向移动,直到到达另一个点。具体来说,可以从 (x1, y1) 直接移动到 (x2, y1) 或者 (x1, y2),但无法直接移动到 (x2, y2)。为了确保任意两个点之间都可以通过这种路径互相到达,小 M 需要增加最少数量的新点。
题解
解题思路
这个问题可以转化为图论中的连通性问题。将每个点看作图中的一个节点,如果两个点在同一行或同一列,则它们之间有一条边相连。我们需要找到图中的连通分量数目,然后最少需要增加的点数就是连通分量数目减一。
具体步骤如下:
- 并查集(Union-Find):使用并查集数据结构来管理点的连接关系,方便快速合并和查找不同连通分量。
- 建立行和列的映射:
- 使用两个字典,
row和col,分别存储每一行和每一列上的点的索引。 - 遍历所有点,将具有相同
x坐标的点加入同一行,具有相同y坐标的点加入同一列。
- 使用两个字典,
- 合并连通分量:
- 对于每一行中的所有点,依次将它们合并到第一点所在的连通分量。
- 对于每一列中的所有点,依次将它们合并到第一点所在的连通分量。
- 计算连通分量:
- 最终,通过并查集找到所有点的根节点,统计不同的根节点数量,即为连通分量的数目。
- 得出结果:
- 最少需要增加的点数为连通分量数目减一。
代码实现
from collections import defaultdict
def solution(n: int, points: list) -> int:
p = [i for i in range(n)]
def find(x):
if x != p[x]:
p[x] = find(p[x])
return p[x]
def union(x, y):
x, y = find(x), find(y)
if x != y:
p[y] = x
row = defaultdict(list)
col = defaultdict(list)
for i, (x, y) in enumerate(points):
row[x].append(i)
col[y].append(i)
for v in row.values():
for i in range(1, len(v)):
union(v[0], v[i])
for v in col.values():
for i in range(1, len(v)):
union(v[0], v[i])
return len(set(find(i) for i in range(n))) - 1
if __name__ == '__main__':
print(solution(2, [[1, 1], [2, 2]]) == 1)
print(solution(3, [[1, 2], [2, 3], [4, 1]]) == 2)
print(solution(4, [[3, 4], [5, 6], [3, 6], [5, 4]]) == 0)