149小A的移动点

问题描述

小 M 有n个点,每个点的坐标为 ($x_i$, $y_i$)。她可以从一个点出发,沿着坐标轴方向移动,直到到达另一个点。具体来说,可以从 (x1, y1) 直接移动到 (x2, y1) 或者 (x1, y2),但无法直接移动到 (x2, y2)。为了确保任意两个点之间都可以通过这种路径互相到达,小 M 需要增加最少数量的新点。

题解

解题思路

这个问题可以转化为图论中的连通性问题。将每个点看作图中的一个节点,如果两个点在同一行或同一列,则它们之间有一条边相连。我们需要找到图中的连通分量数目,然后最少需要增加的点数就是连通分量数目减一。

具体步骤如下:

  1. 并查集(Union-Find):使用并查集数据结构来管理点的连接关系,方便快速合并和查找不同连通分量。
  2. 建立行和列的映射
    • 使用两个字典,rowcol,分别存储每一行和每一列上的点的索引。
    • 遍历所有点,将具有相同x坐标的点加入同一行,具有相同y坐标的点加入同一列。
  3. 合并连通分量
    • 对于每一行中的所有点,依次将它们合并到第一点所在的连通分量。
    • 对于每一列中的所有点,依次将它们合并到第一点所在的连通分量。
  4. 计算连通分量
    • 最终,通过并查集找到所有点的根节点,统计不同的根节点数量,即为连通分量的数目。
  5. 得出结果
    • 最少需要增加的点数为连通分量数目减一。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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)

149小A的移动点
https://kongshuilinhua.github.io/2024/12/23/149小A的移动点/
作者
FireFLy
发布于
2024年12月23日
许可协议