关于最小生成树的话,其实很早之前就接触了,当时也写了一篇关于最小生成树的文章,但一直没有好好刷题。
接下来几天会持续更新维护KB-最小生成树专题
最小生成树的算法没有其他算法那么复杂,算法思想比较简单,代码也比较容易。
常见的最小生成树算法由Kruskal
算法和Prim
算法。
1.Kruskal算法 -- 时间复杂度\(O(m * log m)\)
算法思想:
- 建立一个并查集,每个点构成一个集合;
- 将边进行从小到大进行排序,依次扫描边edge(u,v,w);
- 如果u和v 属于同一个集合,那么跳过这轮循环;
- 如果不属于同一个集合,则把u、v合并为同一个集合;
- 当集合中的顶点数为n或者扫遍所有边edge,则构成最小生成树。
#include
#include
#include
using namespace std;
const int manx=1e5+5; //对应顶点数目
const int mamx=1e5+5; //对应边的数目
int n,m,u,v,total=1;
struct edge{
int start,to;
long long val;
}bian[mamx];
int a[manx];
long long ans;
int find(int x) //并查集
{
if(a[x]==x) return x;
else return a[x]=find(a[x]);
}
bool cmp(edge x,edge y)
{
return x.val
2.Prim算法
Prim算法跟最短路中的Dijkstra 算法思想相近,有兴趣的可以了解一下:
https://www.cnblogs.com/RioTian/p/12597634.html
算法思想:
1.把1作为起点加入最小生成树集合S;
2.在未访问过的集合T中找出一点距离集合S最近的点,并在T中将其剔除,移入S中;
3.重复2步骤,直到所有点加入S。
朴素算法 时间复杂度 \(O(n^2)\)
const int inf=2147483647;
int a[manx][manx], d[manx], n, m, ans;
bool vis[manx];
void prim(){
for(int i=1;i<=n;i++) d[i]=inf,vis[i]=0,a[i][i]=0; //初始化各数组
d[1]=0; //1作为起点
for(int i=1;i
Prim算法使用堆优化可达到与Kruscal一样的复杂度 \(O(m * log m)\)
#include
#include
#include
#include
#include
using namespace std;
const int manx=5e3+5;
const int mamx=1e5+5;
int k,n,m,cnt,sum,a,b,c;
int head[manx],dis[manx],vis[manx];
struct node{
int v,w,next;
}e[mamx];
typedef pair p;
priority_queue,greater
>q; //堆优化
void add(int u, int v, int w) //链式前向星建图
{
e[++k].v=v;
e[k].w=w;
e[k].next=head[u];
head[u]=k;
}
int main()
{
memset(dis,127,sizeof(dis));
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(R i=1;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
//无向图 add(b,a,c);
}
dis[1]=0; //一般将1作为最小生成树扩展的起点
q.push(make_pair(0,1));
while(!q.empty() && cnt
尽管堆优化,但不如直接使用Kruskal算法更加方便,因此,稀疏图用Kruskal,稠密图用Prim 。