Prim
题目链接:https://www.acwing.com/problem/content/description/860/
时间复杂度:O( n 2 n^2 n2)
适用于:稠密图,如果用堆优化也可用于稀疏图,但是稀疏图用Kruskal算法更加实用
算法思想:
和
d
i
j
k
s
t
r
a
dijkstra
dijkstra 类似,循环
n
n
n 次,每次找到一个距离树最近的点,把该点加入树中,并用该点去更新其他点到树的距离
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 510,INF = 0x3f3f3f3f;
int n,m;
int g[N][N],dist[N]; // 存储各个节点到生成树的距离
bool vis[N]; // 节点是否被加入到生成树中
int prim()
{
memset(dist,0x3f,sizeof dist);
int res = 0;
for(int i = 0; i < n; i++) // 每次循环选出一个点加入到生成树
{
int t = -1;
for(int j = 1; j <= n; j++) // 每个节点一次判断
{
if(!vis[j] && (t == -1 || dist[t] > dist[j])) // 如果没有在树中,且到树的距离最短,则选择该点
{
t = j;
}
}
vis[t] = true;
if(i && dist[t] == INF) return INF; // 不联通
if(i) res += dist[t];
for(int j = 1; j <= n; j++) dist[j] = min(dist[j],g[t][j]); // 更新生成树外的点到生成树的距离
}
return res;
}
int main()
{
scanf("%d%d",&n,&m);
memset(g,0x3f,sizeof g);
while(m--)
{
int a,b,w;
scanf("%d%d%d",&a,&b,&w);
g[a][b] = g[b][a] = min(g[a][b],w);
}
int t = prim();
if(t == INF) printf("impossible\n");
else printf("%d\n",t);
return 0;
}
Kruskal
题目链接:https://www.acwing.com/problem/content/description/861/
时间复杂度:O( m l o g n mlogn mlogn)
适用于:稀疏图
算法思想:
利用贪心+并查集,先初始化并查集,将权值从小到大排序,循环
m
m
m 次,每次看当前边的两个点是否在树上,如果不在就加入,最后树上只要有
n
−
1
n-1
n−1 条边就说明存在最小生成树
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 200010;
struct Edge{
int a;
int b;
int w;
bool operator < (const Edge &t)const
{
return w < t.w; // 在结构体里重载运算符 w < t.w 降序 w > t.w 升序
}
}edges[N];
int n,m;
int p[N];
int find(int x)
{
if(p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
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); // 将边的权重按照大小排序
int cnt = 0,res = 0; // res记录最小生成树的树边权重之和,cnt记录的是全部加入到树的集合中边的数量(可能有多个集合)
for(int i = 1; i <= n; i++) p[i] = i; // 初始化并查集
for(int i = 0; i < m; i++)
{
int a,b,w;
a = find(edges[i].a);
b = find(edges[i].b);
w = edges[i].w;
if(a != b)
{
cnt++;
res += w;
p[a] = b;
}
}
if(cnt < n-1) printf("impossible\n"); // 树中有n个节点便有n-1条边,如果cnt不等于n-1的话,说明无法生成有n个节点的树
else printf("%d\n",res);
return 0;
}