LeetCode-1584. Minimum cost to connect all points

description

Give you a points array, representing some points on the 2D plane, where points[i] = [xi, yi].

The cost of connecting point [xi, yi] and point [xj, yj] is the Manhattan distance between them: |xi-xj| + |yi-yj|, where |val| represents the absolute value of val.

Please return the minimum total cost of connecting all points. Only when there is one simple path between any two points, all points are considered to be connected.

 

Example 1:

Input: points = [[0,0],[2,2],[3,10],[5,2],[7,0]]
Output: 20
Explanation:

We can connect all the points as shown in the figure above to get the minimum total cost, and the total cost is 20.
Note that there is only one path between any two points to reach each other.
Example 2:

Input: points = [[3,12],[-2,5],[-4,1]]
Output: 18
Example 3:

Input: points = [[0,0],[1,1],[1,0],[-1,1]]
Output: 4
Example 4:

Input: points = [[-1000000,-1000000],[1000000,1000000]]
Output: 4000000
Example 5:

Input: points = [[0,0]]
Output: 0
 

prompt:

1 <= points.length <= 1000
-106 <= xi, yi <= 106
All points (xi, yi) are different.

Source: LeetCode
Link: https://leetcode-cn.com/problems/min-cost-to-connect-all-points/

 

Solve

    class UnionFind {
    public:
        UnionFind(int n) : count(n) {
            parent.reserve(count + 1);
            for (int i = 0; i <= count; ++i) {
                parent[i] = i;
            }
            rank.resize(count + 1, 1);  // 初始每个的层级均为1
        }

        bool isConnected(int p, int q) {
            return find(p) == find(q);
        }

        void unionElements(int p, int q) {
            int proot = find(p);
            int qroot = find(q);
            if (proot == qroot) {
                return;
            }

            if (rank[proot] < rank[qroot]) {
                parent[proot] = qroot;
            } else if (rank[proot] > rank[qroot]) {
                parent[qroot] = proot;
            } else {
                // rank[proot] == rank[qroot]
                parent[proot] = qroot;
                ++rank[qroot];  // proot ”挂载“到qroot下面,本来两个层级一致,现在需要增加1
            }
        }

    private:
        int find(int p) {
            while (p != parent[p]) {
                parent[p] = parent[parent[p]]; // 路径压缩优化,请细品
                p = parent[p];
            }
            return p;
        }

    private:
        std::vector<int> parent;
        int count;
        std::vector<int> rank;
    };

    struct Edge {
        Edge() {}

        Edge(int a, int b, int w) : v(a), w(b), weight(w) {};
        int v = 0; // 顶点v
        int w = 0; // 顶点w
        int weight = 0; // 边v-w的权值
    };

    class Solution {
    public:
        // 方法一,构造图,prim算法计算最小生成树
        int minCostConnectPoints_1e(vector<vector<int>> &points) {
            const int n = points.size();
            if (n <= 1) {
                return 0;
            }
            vector<vector<int>> graph(n, vector<int>(n, INTMAX)); // 邻接矩阵存储图
            // 构造图
            for (int i = 0; i < n; ++i) {
                for (int j = i + 1; j < n; ++j) {
                    graph[i][j] = graph[j][i] = distance(points[i], points[j]);
                }
            }

            // 求最小生成树
            vector<bool> visited(n, false); // 标记节点是否被访问,即是否加入最小生成树中
            int weight = 0; // 最小生成树权值
            visited[0] = true;  // 首先将节点0添加进去
            vector<int> lowcost = graph[0];
            for (int i = 1; i < n; ++i) {
                int index;
                int minEdge = INTMAX;
                // 寻找还未加入最小生成树中节点到最小生成树中节点权值最小边
                for (int k = 0; k < n; ++k) {
                    if (visited[k]) {
                        continue;
                    }
                    if (lowcost[k] < minEdge) {
                        minEdge = lowcost[k];
                        index = k;
                    }
                }
                weight += minEdge;
                visited[index] = true; // 将当前最小权值得边加入最小生成树中

                // 更新最小边
                for (int k = 0; k < n; ++k) {
                    if (visited[k]) {
                        continue;
                    }
                    if (graph[index][k] < lowcost[k]) {
                        lowcost[k] = graph[index][k];
                    }
                }
            }

            // 返回最小生成树的权值
            return weight;
        }

        // 方法二,构造图,kruskal算法计算最小生成树, 效率较低
        int minCostConnectPoints(vector<vector<int>> &points) {
            const int n = points.size();
            if (n <= 1) {
                return 0;
            }
            // 存储所有边的权值,无向图对称性,只需要存储一半即可
            vector<Edge> edges;
            edges.reserve(n * (n - 1) / 2);
            // 计算边权值
            for (int i = 0; i < n; ++i) {
                for (int j = i + 1; j < n; ++j) {
                    edges.emplace_back(i, j, distance(points[i], points[j]));
                }
            }

            //
            std::sort(edges.begin(), edges.end(),
                      [](const auto &lhs, const auto &rhs) noexcept { return lhs.weight < rhs.weight; });
            UnionFind uf(n);
            int res = 0;
            int edgeNum = 0;
            for (auto &edge : edges) {
                if (uf.isConnected(edge.v, edge.w)) {
                    continue;
                }
                res += edge.weight;
                uf.unionElements(edge.v, edge.w);
                ++edgeNum;
                if (edgeNum == n - 1) {
                    // 已选取n-1条边构成最小生成树后,终止寻找过程
                    return res;
                }
            }

            return res;
        }

    private:
        const int INTMAX = std::numeric_limits<int>::max();

        inline int distance(const vector<int> &point1, const vector<int> &point2) const {
            return abs(point1[0] - point2[0]) + abs(point1[1] - point2[1]);
        }
    };

 

Guess you like

Origin blog.csdn.net/u010323563/article/details/112852125