最小生成树-Prim算法与Kruscal算法
以下文章:
\(N\)代表点数,\(M\)代表边数。
Prim算法
1.找到不在最小生成树中的距离最近的点t
2.将点t加入到集合中
3.利用点t更新其他不在最小生成树中的点。
const int N = 550;
int dist[N];
bool st[N];
int g[N][N];
int prim(){
memset(dist , INF ,sizeof(dist));
for(int i = 0;i < n;i++){
int t = -1;
for(int j = 1;j <= n;j++){
if(!st[j] && (t == -1 || dist[t] > dist[j])
t = j;
}
st[t] = true;
if(i && dist[t] == INF) return INF;
if(i) res += dist[t];
for(int j = 1;j <= n;j++){
dist[j] = fmin(dist[j] , g[t][j];
}
}
return res;
}
Kruscal算法
1.将所有边按照权重大小排序 $ O(M*logM) $
2.枚举每条边a,b权重c。
3.如果当前a,b不连通,则加入最小生成树的集合。
难点:如何快速得知两个点是否属于同一集合。 :并查集。
int find(int x){ //查询点x所在的集合
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
void union(int a,int b){ //将a点所属的集合与b点所属的集合合并。
int x = find(a);
int y = find(b);
p[x] = y;
}
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n,m;
int p[N];
struct Edge{
int a,b,w;
}edges[N];
int cmp(Edge a, Edge b){
return a.w < b.w;
}
int find(int x){
if(x != p[x]) p[x] = find(p[x]);
return p[x];
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i = 0;i < m;i++){
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
edges[i] = {a,b,w};
}
sort(edges , edges + m, cmp); //按照权重从小到大排序
for(int i = 1;i <= n;i++) p[i] = i;
int res = 0,cnt = 0;
for(int i = 0;i < m;i++){
int a = edges[i].a,b = edges[i].b,w = edges[i].w;
a = find(a); b = find(b);
if(a != b){
p[a] = b;
res += w;
cnt++;
}
}
if(cnt < n - 1) puts("impossible"); // 存在负环
else printf("%d\n",res);
return 0;
}