最小生成树

0.前置知识

存图方式(邻接表邻接矩阵),并查集(嘚瑟一下,我自己专门写了一篇)。
不会的快进入链接学习吧!

1.引入

生成树,就是从一个图中选中 n − 1 n-1 n1条边,使得这些边构成一棵树,并包含图中的所有节点。
最小生成树,就是找到一种生成树,使得这个生成树的边权和最小。

2.生成方式一:prim

这种方法有点类似Dijstra,就是每次从所有 v i s vis vis过的点遍历能达到的边,从其中选择一条最小的,加入生成树。


假设我们有这么一张图:
弟弟

就从0号点开始吧:

在这里插入图片描述

找到从 0 0 0出发的最小的边: [ 0 , 2 ] [0,2] [0,2],边权为 3 3 3,那么对 2 2 2号点进行标记。

然后从 0 0 0号和 2 2 2号节点继续找,发现最小的是 [ 2 , 4 ] [2,4] [2,4]边,那么就标记 4 4 4号节点。
在这里插入图片描述

然后是 [ 4 , 1 ] [4,1] [4,1] 1 1 1号。
在这里插入图片描述

以此类推,最后就生成出来了这样一个图:

在这里插入图片描述

把没有标记的边删掉,就是最小生成树。

这就是prim算法 ,代码我不会写在后面。

3.生成方式二:kruskal

这个算法本质上就是把所有边按照边权排序,然后直接爆炸按顺序判断要不要加进生成树里。
kruskal算法使用了一种极速闪电致命又自杀的东西:并查集。比 l o g n log n logn还要快,是 ∗ l o g n *log n logn
他有多快呢?

n n n他的复杂度
( 1 , 2 ] (1,2] (1,2] 1 1 1
( 2 , 4 ] (2,4] (2,4] 2 2 2
( 4 , 16 ] (4,16] (4,16] 3 3 3
( 16 , 65536 ] (16,65536] (16,65536] 4 4 4
( 65536 , 2 65536 ] (65536,2^{65536}] (65536,265536] 5 5 5

所以看到了吗,n是 2 65536 2^{65536} 265536的时候复杂度还只有5。
2 65536 2^{65536} 265536有多大,我把他拷进来的时候整个电脑卡死了。他有19729位。想通了吧?
详细讲解还是看看这个吧(又嘚瑟)


好了我们在建一个图模拟一下吧 (其实还是刚才那个图,搞了搞边权)
在这里插入图片描述

太好了,我们先给边排序。最小的是 [ 2 , 3 ] [2,3] [2,3],把他揪出来,判断一下。怎么判断呢?首先拿并查集看一看,这个边连接的两个点在不在同一个集合内,不在的话就把这条边加入生成树,然后把两边都合并到一个集合里。否则的话就直接忽略。这一条边符合要求,加进并查集里,现在{ 2 , 3 2,3 2,3}是一个集合,剩下都是独立的。

在这里插入图片描述
现在最小的有 [ 1 , 4 ] [1,4] [1,4] [ 4 , 5 ] [4,5] [4,5],我们都判断一下,都可以。
在这里插入图片描述

然后就是 [ 0 , 4 ] [0,4] [0,4] [ 2 , 4 ] [2,4] [2,4],依然都是可以的。
在这里插入图片描述
这样,一颗活灵活现的生成树就出现了。

好了!

4.代码(luoguP3366

kruskal的代码又短又易于理解,甚至可以直接用数组存边,所以他非常好写,推荐。

prim
#include "iostream"
#include "queue"
#include "cstring"
using namespace std;
#define maxm 5000010
#define maxn 1000010
#define inf 0x3f3f3f3f
int ans=0;
struct node{
    int v,next,c;
}e[maxm<<1];
int h[maxn],tot,n,m;
void adde(int u,int v,int c){
    tot++;
    e[tot].v=v;
    e[tot].c=c;
    e[tot].next=h[u];
    h[u]=tot;
}
int dis[maxn];
bool vis[maxn];
struct qu{
    int id,dis;
    bool operator < (const qu t)const{
        return dis>t.dis;
    }
};
priority_queue<qu> q;
void dijk(int s){
    memset(dis,0x3f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    while(!q.empty())q.pop();
    qu t;
    t.dis=0;
    t.id=s;
    dis[s]=0;
    q.push(t);
    while(!q.empty()){
        t=q.top();q.pop();
        if(vis[t.id])continue;
        vis[t.id]=true;
        ans+=t.dis;
        for(int j=h[t.id];j;j=e[j].next){
            int v=e[j].v;
            if(!vis[v]&&dis[v]>e[j].c){
                dis[v]=e[j].c;
                qu now;
                now.id=v;
                now.dis=dis[v];
                q.push(now);
            }
        }
    }
}
int main(){
    int s=1;
    cin>>n>>m;
    int u,v,c;
    for(int i=1;i<=m;i++){
        cin>>u>>v>>c;
        adde(u,v,c);
adde(v,u,c);
    }
    dijk(s);
cout<<ans<<endl;
    return 0;
}
kruskal
#include "iostream"
#include "algorithm"
using namespace std;
int n,m,tot,sum,f[5001];
struct node{
	int u,v,c;
}a[200010];
int cmp(const node &u,const node &v){
	return u.c<v.c;
}
int getf(int u){
	if(f[u]==u)return u;
	return f[u]=getf(f[u]);
}
int main(){
	cin>>n>>m;
	if(m<n-1){cout<<"orz"<<endl;return 0;}
	for(int i=1;i<=m;i++)cin>>a[i].u>>a[i].v>>a[i].c;
	for(int i=1;i<=n;i++)f[i]=i;
	sort(a+1,a+m+1,cmp);
	for(int i=1;i<=m;i++){
		int fu=getf(a[i].u),fv=getf(a[i].v);
		if(fu!=fv){
            f[fu]=fv;
			sum++;
			tot+=a[i].c;
		}
		if(sum==n-1)break;
	}
	cout<<tot<<endl;
}

萌新刚学,码风请喷

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值