什么是最小生成树?
prim和kurskal都是用来求最小生成树的,所以我们需要先了解什么是最小生成树。
一句话定义:最小生成树就是求遍历图中所有点的边权累加和最小。 它和最短路问题有点像。但是最短路问题并不一定遍历到所有的点,但是最小生成树会。
例如下图:
首先我们需要给所有的边排个序(因为要求最小边权累加和)
然后我们就开始连边: 图论必备算法之一:最小生成树 (易懂至极)_最小生成树算法 只能用于无向图吗?-CSDN博客
为什么要用并查集算法。
并查集算法用来判断两个点是否联通(直接联通或间接连通),如果没有联通那么就联通,并将边权值进行累加。如果已经联通(间接联通),那就没必要再联通(直接联通),这样就可以少累加一个边权值。
858. Prim算法求最小生成树
样例图:
我们初始的时候把所有点到集合的距离初始化为正无穷:
我们先从随便挑一个点,假设我们选节点1:
我们通过节点1来更新其他点到集合的距离。
所谓其他点到集合的距离就是说其他点到集合的较短路径。
2到集合中点的距离是1,3到集合中点的距离是2,4到集合中点的距离是3:
然后我们在剩下的节点中1找到一个较小的节点,通过这个节点去更新其他点到集合中点的距离。
我们发现节点2的权值较小,所以我们选中节点2,此时去更新其他节点到集合中的点的距离。
节点3的最小路径是3,不用更新,节点2的最小路径是1,也不用更新。此时把节点2加入到集合当中:
我们从剩下的节点找权值较小的,节点3的权值比就节点4小,选中节点3,更新其他点到集合中点的距离。节点4的权值是3,已经是最小的了,不用更新,把节点3加入到集合当中。
只剩节点4了,把节点4也加入到集合当中:
#include<bits/stdc++.h>
using namespace std;
const int N = 510,INF=0x3f3f3f3f;
int dist[N];
int g[N][N];
bool st[N];
int n, m;
int prime()
{
memset(dist,0x3f,sizeof dist); //第一步先把所有距离初始化为正无穷
//第二步n次迭代
int res = 0; //存最小生成树的所有边的长度之和
for (int i = 0; i < n; i++)
{
//每次找当前集合i的距离最小的点
int t = -1; //-1表示当前没有找到任何一点
for (int j = 1; j <= n; j++)
if (!st[j] && (t == -1 || dist[t] > dist[j])) //如果当前边不确定是最短边 并且 没有找到任何一个点 或者t的距离大于j的距离
t = j; //t的距离更新为j的距离
if (i && dist[t] == INF)return INF; //如果不是第一个1点 并且 当前边仍然是INF 说明存在不联通情况 ,即不存在最小生成树
if (i) res += dist[t]; //不是第一个点
//更新其他点到集合的距离
for (int j = 1; j <= n; j++) dist[j] = min(dist[j],g[t][j]);
st[t] = true; //当前点加入到集合中
}
return res;
}
int main()
{
cin >> n >> m;
memset(g, 0x3f, sizeof g);
while (m--)
{
int a, b, c;
cin >> a >> b >> c;
g[a][b] = g[b][a] = min(g[a][b], c);
}
int t = prime();
if (t == 0x3f3f3f3f) puts("impossible");
else cout << t;
return 0;
}
859. Kruskal算法求最小生成树
第一步,首先我们给所有的边都排个序。
第二步,枚举所有的边,判断a,b之间是否连通,如果不连通,那么就给a,b之间连上一条边(加上权值c)。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m,res,cnt;
int p[N];
int find(int x)
{
if(p[x]!=x)p[x]=find(p[x]);
return p[x];
}
struct Edge
{
int a,b,w;
bool operator<(const Edge &W)const
{
return w<W.w;
}
}edges[N];
int main()
{
//cin.tie(nullptr)->sync_with_stdio(false);
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b,w;
cin>>a>>b>>w;
edges[i]={a,b,w};
}
sort(edges,edges+m);//给边排个序
for(int i=1;i<=n;i++)p[i]=i;
//从小到大枚举所有边
for(int i=0;i<m;i++)
{
int a=edges[i].a,b=edges[i].b,w=edges[i].w;
//判断一下a,b是否连通
a=find(a),b=find(b);
if(a!=b)
{
p[a]=b; //把a,b所在的集合合并
res+=w; //存最小生成树中所有树边的权重之和
cnt++; //统计一下加了多少条边
}
}
if(cnt<n-1)puts("impossible"); //如果小于n-1,说明边没加够,当前子集不连通
else cout<<res<<endl;
return 0;
}