小A的移动点 | 豆包MarsCode AI刷题

68 阅读3分钟

问题描述

小M有n个点,每个点的坐标为(xi,yi)。她可以从一个点出发,平行于坐标轴移动,直到到达另一个点。具体来说,她可以从 (x1,y1) 直接到达 (x2,y1) 或者 (x1,y2),但无法直接到达 (x2,y2)。为了使得任意两个点之间可以互相到达,小M可以选择增加若干个新的点。

你的任务是计算最少需要增加多少个点,才能保证任意两个点之间可以通过平行于坐标轴的路径互相到达。


测试样例

样例1:

输入:n = 2, points = [[1, 1], [2, 2]]
输出:1

样例2:

输入:n = 3, points = [[1, 2], [2, 3], [4, 1]]
输出:2

样例3:

输入:n = 4, points = [[3, 4], [5, 6], [3, 6], [5, 4]]
输出:0

要解决这个问题,我们需要通过分析点之间的连接性,最小化新增点的数量,确保所有点能够通过平行于坐标轴的路径互相到达。这是一道与图连通性相关的几何问题。


以下为解题思路

解题思路

  1. 点的连接规则

    • 两点可以互相到达的条件是它们在同一行 (x1=x2x_1 = x_2) 或同一列 (y1=y2y_1 = y_2)。换句话说,我们需要把这些点划分为同一行或同一列的连通块
  2. 连通块的定义

    • 任意两点属于同一个连通块,当且仅当它们之间通过一系列满足上述条件的点可以连通。
    • 每个连通块内的点可以直接或间接互相到达。
  3. 目标

    • 计算连通块的数量 kk。
    • 我们需要保证所有点属于同一个连通块,因此至少需要新增 k−1k - 1 个点。
  4. 算法设计

    • 将问题建模为一个图问题,其中每个点是一个节点:

      • 如果两点 (x1,y1)(x_1, y_1) 和 (x2,y2)(x_2, y_2) 满足 x1=x2x_1 = x_2 或 y1=y2y_1 = y_2,它们之间有一条边。
    • 使用并查集深度优先搜索 (DFS) 找到所有连通块。

    • 结果是连通块的数量减去 1,即 k−1k - 1。

  5. 实现方式

    • 利用并查集(Union-Find)高效地处理连通性问题。

    • 构建虚拟节点以减少复杂度:

      • 对于每个 xix_i 和 yiy_i,将点的 xx-坐标和 yy-坐标分开存储,并将其归类到同一连通块。

算法实现

以下是基于并查集的实现:

class UnionFind:
    def __init__(self):
        self.parent = {}

    def find(self, x):
        if x not in self.parent:
            self.parent[x] = x
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]

    def union(self, x, y):
        root_x = self.find(x)
        root_y = self.find(y)
        if root_x != root_y:
            self.parent[root_x] = root_y

def min_additional_points(n, points):
    uf = UnionFind()

    # 建立点的连通性:按 x 和 y 坐标分别分类
    for x, y in points:
        # 将点的 x 坐标和 y 坐标虚拟为节点,并归并
        uf.union(f"x_{x}", f"y_{y}")

    # 查找所有独立的连通块
    connected_components = set()
    for x, y in points:
        connected_components.add(uf.find(f"x_{x}"))
        connected_components.add(uf.find(f"y_{y}"))

    # 连通块的数量
    num_connected_components = len(connected_components)
    return num_connected_components - 1

测试用例

样例 1:

输入:

n = 2
points = [[1, 1], [2, 2]]
print(min_additional_points(n, points))  # 输出:1

样例 2:

输入:

n = 3
points = [[1, 2], [2, 3], [4, 1]]
print(min_additional_points(n, points))  # 输出:2

样例 3:

输入:

n = 4
points = [[3, 4], [5, 6], [3, 6], [5, 4]]
print(min_additional_points(n, points))  # 输出:0

复杂度分析

  1. 时间复杂度

    • 构建并查集和执行 unionunion 操作:O(n⋅α(n))O(n \cdot \alpha(n)),其中 α(n)\alpha(n) 是反阿克曼函数的复杂度,几乎是常数。
    • 遍历所有点和计算连通块:O(n)O(n)。
    • 总复杂度:O(n)O(n)。
  2. 空间复杂度

    • 并查集的存储消耗:O(n)O(n),存储所有虚拟节点。

关键点总结

  • 使用并查集解决坐标平面上的连通性问题。
  • 虚拟节点将 xx-坐标和 yy-坐标统一建模为连通性问题。
  • 输出结果为 连通块数量−1\text{连通块数量} - 1,对应最少新增点的数量。