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

130 阅读2分钟

问题描述

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

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

问题转化

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

思路

  • 使用并查集(Union-Find)对点进行归类,判断哪些点在同一个连通块中。
  • 横纵坐标相同的点可以直接归类到同一连通块。
  • 每个独立的连通块需要至少一个新增点与其它连通块连接。
  • 最终新增点数目 = 连通块数目 - 1。

代码实现

#include <iostream>
#include <vector>
#include <unordered_map>
#include <unordered_set>

using namespace std;

class UnionSet {
private:
    vector<int> parent;
    vector<int> rank;

public:
    UnionSet(int n) {
        parent.resize(n);
        rank.resize(n, 0);
        for (int i = 0; i < n; ++i) {
            parent[i] = i;
        }
    }

    int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]); // 路径压缩
        }
        return parent[x];
    }

    void unite(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX != rootY) {
            if (rank[rootX] < rank[rootY]) {
                parent[rootX] = rootY;
            } else if (rank[rootX] > rank[rootY]) {
                parent[rootY] = rootX;
            } else {
                parent[rootY] = rootX;
                rank[rootX]++;
            }
        }
    }
};

int solution(int n, vector<vector<int>> points) {
    unordered_map<int, vector<int>> xMap, yMap;
    for (int i = 0; i < n; ++i) {
        xMap[points[i][0]].push_back(i); // 按横坐标分组
        yMap[points[i][1]].push_back(i); // 按纵坐标分组
    }

    UnionSet uf(n);

    // 按横坐标连通
    for (const auto& [x, indices] : xMap) {
        for (int i = 1; i < indices.size(); ++i) {
            uf.unite(indices[0], indices[i]);
        }
    }

    // 按纵坐标连通
    for (const auto& [y, indices] : yMap) {
        for (int i = 1; i < indices.size(); ++i) {
            uf.unite(indices[0], indices[i]);
        }
    }

    // 统计连通块数量
    unordered_set<int> uniqueRoots;
    for (int i = 0; i < n; ++i) {
        uniqueRoots.insert(uf.find(i));
    }

    return uniqueRoots.size() - 1; // 最少需要新增的点数
}

int main() {
    cout << (solution(2, {{1, 1}, {2, 2}}) == 1) << endl;
    cout << (solution(3, {{1, 2}, {2, 3}, {4, 1}}) == 2) << endl;
    cout << (solution(4, {{3, 4}, {5, 6}, {3, 6}, {5, 4}}) == 0) << endl;
    return 0;
}

算法分析

  1. 分组操作:O(n)

    • 遍历所有点,将点分组到 xMapyMap
  2. 并查集操作:近似 O(n)

    • 连通操作和查询操作的时间复杂度接近 O(1),总共执行 2n 次。
  3. 统计连通块:O(n)

    • 遍历所有点,统计连通块根节点。

综上所述:总复杂度:O(n)