给你一个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
-10^6 <= xi, yi <= 10^6
- 所有点
(xi, yi)
两两不同。
解答
并查集+Kruskal算法。首先将图中所有可能的连接及对应边长存储起来,并按边长大小排序,从小到大贪心地将对应边加入图中并检查是否有连通分量(维护并查集),当加入n-1
条边时,对应的最小生成树构建完成。
struct Edge{
int x, y, dist;
Edge(int x, int y, int dist) : x(x), y(y), dist(dist){
}
};
class DSU{
public:
vector<int> parent, rank;
DSU(int n){
rank.resize(n, 1);
parent.resize(n);
for(int i = 0; i < n; i++){
parent[i] = i;
}
}
int find(int i){
if(i != parent[i]){
int temp = find(parent[i]);
parent[i] = temp;
}
return parent[i];
}
bool merge(int i, int j){
int pi = find(i);
int pj = find(j);
if(pi == pj){
return false;
}
else{
if(rank[pi] > rank[pj]){
swap(pi, pj);
}
rank[pj] += rank[pi];
parent[pi] = pj;
}
return true;
}
};
class Solution {
public:
int minCostConnectPoints(vector<vector<int>>& points) {
int n = points.size();
DSU dsu(n);
vector<Edge> edges;
auto dist = [&](int x, int y){
return abs(points[x][0] - points[y][0]) + abs(points[x][1] - points[y][1]);
};
for(int i = 0; i < n; i++){
for(int j = i + 1; j < n; j++){
edges.emplace_back(i, j, dist(i, j));
}
}
sort(edges.begin(), edges.end(), [](Edge& a, Edge& b){
return a.dist < b.dist;
});
int result = 0, cnt = 0;
for(auto& e : edges){
int distance = e.dist;
int x = e.x;
int y = e.y;
if(dsu.merge(x, y)){
result += distance;
cnt++;
if(cnt == n - 1)
break;
}
}
return result;
}
};