LeetCode - 1584. 连接所有点的最小费用
作者:互联网
描述
给你一个points 数组,表示 2D 平面上的一些点,其中 points[i] = [xi, yi] 。
连接点 [xi, yi] 和点 [xj, yj] 的费用为它们之间的 曼哈顿距离 :|xi - xj| + |yi - yj| ,其中 |val| 表示 val 的绝对值。
请你返回将所有点连接的最小总费用。只有任意两点之间 有且仅有 一条简单路径时,才认为所有点都已连接。
示例 1:
输入:points = [[0,0],[2,2],[3,10],[5,2],[7,0]]
输出:20
解释:
我们可以按照上图所示连接所有点得到最小总费用,总费用为 20 。
注意到任意两个点之间只有唯一一条路径互相到达。
示例 2:
输入:points = [[3,12],[-2,5],[-4,1]]
输出:18
示例 3:
输入:points = [[0,0],[1,1],[1,0],[-1,1]]
输出:4
示例 4:
输入:points = [[-1000000,-1000000],[1000000,1000000]]
输出:4000000
示例 5:
输入:points = [[0,0]]
输出:0
提示:
1 <= points.length <= 1000
-106 <= xi, yi <= 106
所有点 (xi, yi) 两两不同。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/min-cost-to-connect-all-points/
求解
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]);
}
};
标签:vector,parent,int,最小,1584,++,points,LeetCode 来源: https://blog.csdn.net/u010323563/article/details/112852125