最小スパニング ツリー (略して MST) は接続グラフのサブグラフです。グラフ内のすべてのノードが含まれ、ツリー (非巡回接続グラフ) です。また、すべてのノードを接続するエッジの重みの合計が最小になることも保証します。 . .
重み付き接続グラフでは、すべてのノードを接続するさまざまな方法がありますが、最小スパニング ツリーの概念では、ツリー全体の重みの合計が最小になるように、最小の重みを持つエッジを選択することが強調されています。
最小スパニング ツリー問題を解くために使用される一般的なアルゴリズムは、Prim のアルゴリズムと Kruskal のアルゴリズムの 2 つです。
1. プリム アルゴリズム: プリム アルゴリズムは開始ノードから開始し、現在のスパニング ツリー ノードと非ツリー ノードを接続する最小重みエッジを徐々に選択し、すべてのノードがスパニング ツリーに含まれるまでノードをスパニング ツリーに追加します。
2. クラスカル アルゴリズム: クラスカル アルゴリズムは、最初にすべてのエッジを重みに従って小さいものから大きいものまで並べ替え、次に重みが最も小さいエッジから始めてスパニング ツリーを 1 つずつ追加します。ただし、ループが形成されないことを保証する必要があります。追加されたエッジの 2 つのノードを同時にコレクション内に含めることはできません。
ps: Kruskal アルゴリズムは和集合検索セットを使用します。
1584. すべてのポイントを接続するための最小コスト
2D 平面上のいくつかの点を表す点配列を与えます。ここで、points[i] = [xi, yi] です。
点 [xi, yi] と点 [xj, yj] を接続するコストは、それらの間のマンハッタン距離: |xi - xj| + |yi - yj| です。ここで、|val| は val の絶対値を表します。
すべての点を結んだ最小の合計コストを返してください。すべてのポイントは、2 つのポイント間に単純なパスが存在し、それが 1 つだけである場合にのみ接続されているとみなされます。
解決
質問の意味によれば、n 個のノードの完全なグラフが得られ、任意の 2 点間の距離がマンハッタン距離になります。ここで、サブグラフの任意の 2 点間に単純なパスが 1 つだけ存在し、このサブグラフのすべてのエッジの合計重みの合計が可能な限り小さくなるような、このグラフ内のサブグラフを取得する必要があります。
任意の 2 点間に単純なパスが存在し、それが 1 つだけであること、およびこのツリーに n 個のノードが含まれていることを満たすツリーは 1 つだけです。このツリーを特定のグラフのスパニング ツリーと呼び、合計の重みが最小のスパニング ツリーを最小スパニング ツリーと呼びます。
最小スパニング ツリーには非常に古典的な解決策があります: Kruskal
コードは以下のように表示されます。
// 定义了 DisjointSetUnion类,这是一个并查集类,用来管理节点的集合关系
class DisjointSetUnion{
private: // 在 private 下的成员变量和函数只能在 类的内部 访问
vector<int> f,rank;
int n;
public:
// 构造函数
DisjointSetUnion(int _n){
// n个节点
n = _n;
rank.resize(n,1);
f.resize(n);
for(int i = 0;i<n;i++){
f[i] = i;
}
}
// 并查集
int find(int x){
// 路径压缩优化
return f[x]==x? x:f[x]=find(f[x]);
}
// 判断x和y是否在一个集合中,如果已经在一个集合了,返回false
// 如果不在一个集合中,通过rank进行合并
int unionSet(int x,int y){
int fx = find(x), fy = find(y);
if(fx == fy){
return false;
}
if(rank[fx] < rank[fy]){
f[fx] = fy;
}else if(rank[fx]>rank[fy]){
f[fy] = fx;
}else {
f[fx] = fy;
rank[fy]++;
}
return true;
}
};
// 定义结构体Edge
struct Edge{
int len,x,y;
// 是一个构造函数 Edge,用来构造Edge对象
// 通过 len(len), x(x) 和 y(y),将传入的参数分别赋值给结构体的成员变量
Edge(int len,int x,int y):len(len),x(x),y(y){
}
};
class Solution {
public:
int minCostConnectPoints(vector<vector<int>>& points) {
// lambda 函数
// [&] 表示 lambda 函数通过引用捕获外部作用域中的变量,
// 这意味着在 lambda 函数内部, 可以访问外部作用域中的变量。
// 计算x和y这两个点之间的曼哈顿距离
auto dist = [&](int x,int y)-> int {
return abs(points[x][0] - points[y][0])
+ abs(points[x][1] - points[y][1]);
};
int n = points.size();
// 创建了一个名为 dsu 的 DisjointSetUnion 类的对象
DisjointSetUnion dsu(n);
vector<Edge> edges;
for(int i = 0;i<n;i++){
for(int j = i+1;j<n;j++){
// 把每两个点之间的距离、起点、终点放入 vector中
// dist(i, j), i, j 是用于构造 Edge 对象的参数
edges.emplace_back(dist(i,j),i,j);
}
}
// 根据权重来排序Edge,权重越小的排在前面
sort(edges.begin(),edges.end(),[](Edge a,Edge b)->int {
return a.len<b.len;});
// num 表示已经连接的点的数量
int ret = 0,num = 1;
//遍历排序后的边,如果两个节点不在同一个集合中,则将它们合并,
// 并将边的长度加入 ret,同时更新 num
for(auto& [len,x,y]:edges){
if(dsu.unionSet(x, y)){
// 合并 x、y
ret += len;
num++;
if(num == n){
break;
}
}
}
return ret;
}
};
1135. すべての都市を最低コストで接続する
あなたが都市インフラストラクチャーの計画者で、地図上に 1 から n までの番号が付けられた n 個の都市があると想像してください。
整数 n と配列 conections が与えられます。connections[i] = [xi, yi,costi] は、都市 xi と都市 yi を接続するのに必要なコストを表します (接続は双方向です)。
都市の各ペア間に少なくとも 1 つのパスを使用してすべての都市を接続する最小コストを返します。n 個の都市すべてに接続できない場合は、-1 を返します。
この最小コストは、使用されるすべての接続のコストの合計である必要があります。
コード:
class Solution {
private:
vector<int> father,rank;
public:
// 路径压缩
int Find(int n){
return father[n] == n? father[n] : father[n] = Find(father[n]);
}
// 按秩合并
int unionSet(int x,int y){
int fx = Find(x), fy = Find(y);
if(fx == fy) return false;
if(rank[fx]<rank[fy]){
father[fx] = fy;
}else if(rank[fx]>rank[fy]){
father[fy] = fx;
}else {
father[fx] = fy;
rank[fy]++;
}
return true;
}
int minimumCost(int n, vector<vector<int>>& connections) {
// 初始化father数组
father.resize(n+1,0);
for(int i = 1;i<=n;i++){
father[i] = i;
}
// 初始化rank数组
rank.resize(n+1,1);
// 根据权重重新排一下 connections
sort(connections.begin(),connections.end(),
[](vector<int> a,vector<int> b){
return a[2]<b[2];
});
int ans = 0, num = 1;
for(auto conn:connections){
if(unionSet(conn[0],conn[1])){
// 如果不是一个集合的,进行合并
ans += conn[2];
num++;
}
if(num == n){
break;
}
}
// 如果没有联通所有的点,则返回-1
return num == n? ans:-1;
}
};