• 史上最全最小生成树算法


    我不信还有人比这个全

    总共三种,大家最熟悉的(Kruskal)(Prim)以及不那么熟悉的(Borůvka)

    时间复杂度:(Kruskal:mathcal{O}(MlogM),Prim:mathcal{O}(N^2),Borůvka:mathcal{O}(MlogN))

    堆优化(Prim)可以到(mathcal{O}(NlogN))

    实现过程:

    (Kruskal:)对所有边排序,从小到大加入,如果会成环就不加入。

    (Prim:)任选一个点加入最小生成树点集(V)中,找到最小的一条边(E),其一端在(V)中,一端不在,将这样的边加入最小生成树中,重复上述操作即可。

    (Borůvka):开始每个点自成一个联通块。 每次对所有联通块找一条边权最小的边(如果有边权相同,就按编号取最小的编号),其中一端在该联通块内而另一端不在,接下来加入这些边并合并联通块。 重复上述操作直到没有联通块可以合并。

    这是(Borůvka)的动态演示,可以说是很清晰了。这个算法就像是(Prim)的进阶版本对吧。。。

    它的(log)是哪来的呢?实际上,它每次加边合并之后联通块数会减少一半,所以总共只要进行(log)次。

    上一个总的代码(堆优化(Prim)用的是(Dijstra)写法)

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
        int f=1,w=0;char x=0;
        while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
        while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
        return w*f;
    }
    const int N=5e3+10;
    const int M=2e5+10;
    
    namespace Kruskal
    {
    	int n,m,ans,fa[N];
    	struct Line{int u,v,dis;} Lin[M];
    	inline bool Cmp(Line x,Line y) {return x.dis<y.dis;}
    	inline int Find(int x) {return fa[x]==x?x:fa[x]=Find(fa[x]);}
    	inline void Kruskal()
    	{
    		sort(Lin+1,Lin+m+1,Cmp);
    		for(int i=1;i<=n;i++) fa[i]=i;
    		for(int i=1;i<=m;i++)
    		{
    			int u=Find(Lin[i].u),v=Find(Lin[i].v);
    			if(u!=v) fa[u]=v,ans+=Lin[i].dis;
    		}
    		printf("%d",ans);
    	}
    	inline int Main()
    	{
    		n=read(),m=read();
    		for(int i=1;i<=m;i++)
    			Lin[i].u=read(),Lin[i].v=read(),Lin[i].dis=read();
    		Kruskal();return 0;
    	}
    }
    namespace Simple_Prime
    {
    	int n,m,ans,Cnt,num_edge;
    	struct Line{int u,v,dis;} Lin[M];
    	int head[M],fa[N],Vis[M],Dis[N],Min[M];
    	struct Edge{int next,to,dis;} edge[M<<1];
    	inline void Add(int from,int to,int dis)
    	{
    		edge[++num_edge].next=head[from];
    		edge[num_edge].dis=dis;
    		edge[num_edge].to=to;
    		head[from]=num_edge;
    	}
    	inline void Simple_Prime(int Now)
    	{
    		memset(Dis,0x3f,sizeof(Dis));Dis[Now]=0;
    		for(int i=head[Now];i;i=edge[i].next)
    			Dis[edge[i].to]=min(Dis[edge[i].to],edge[i].dis);
    		while(++Cnt<n)
    		{
    			int Min=Dis[0];Vis[Now]=1;
    			for(int i=1;i<=n;i++)
    				if(!Vis[i]&&Min>Dis[i]) Now=i,Min=Dis[i];
    			ans+=Min;
    			for(int i=head[Now];i;i=edge[i].next)
    				if(Dis[edge[i].to]>edge[i].dis&&!Vis[edge[i].to])
    					Dis[edge[i].to]=edge[i].dis;
    		}
    		printf("%d",ans);
    	}
    	inline int Main()
    	{
    		n=read(),m=read();
    		for(int i=1;i<=m;i++)
    			Lin[i].u=read(),Lin[i].v=read(),Lin[i].dis=read();
    		for(int i=1;i<=m;i++)
    			Add(Lin[i].u,Lin[i].v,Lin[i].dis),Add(Lin[i].v,Lin[i].u,Lin[i].dis);
    		Simple_Prime(1);return 0;
    	}
    }
    namespace Priority_Prime
    {
    	int n,m,ans,Cnt,num_edge;
    	struct Line{int u,v,dis;} Lin[M];
    	int head[M],fa[N],Vis[M],Dis[N],Min[M];
    	struct Edge{int next,to,dis;} edge[M<<1];
    	inline void Add(int from,int to,int dis)
    	{
    		edge[++num_edge].next=head[from];
    		edge[num_edge].dis=dis;
    		edge[num_edge].to=to;
    		head[from]=num_edge;
    	}
    	priority_queue<pair<int,int> > Q;
    	inline void Priority_Prime(int Now)
    	{
    		memset(Dis,0x3f,sizeof(Dis));
    		Dis[1]=0;Q.push(make_pair(0,1));
    		while(Q.size()&&Cnt<n)
    		{
    			int x=Q.top().second,dis=-Q.top().first;Q.pop();
    			if(Vis[x]) continue;++Cnt,ans+=dis,Vis[x]=1;
    			for(int i=head[x];i;i=edge[i].next)
    				if(Dis[edge[i].to]>edge[i].dis)
    					Dis[edge[i].to]=edge[i].dis,Q.push(make_pair(-edge[i].dis,edge[i].to));
    		}
    		printf("%d",ans);
    	}
    	inline int Main()
    	{
    		n=read(),m=read();
    		for(int i=1;i<=m;i++)
    			Lin[i].u=read(),Lin[i].v=read(),Lin[i].dis=read();
    		for(int i=1;i<=m;i++)
    			Add(Lin[i].u,Lin[i].v,Lin[i].dis),Add(Lin[i].v,Lin[i].u,Lin[i].dis);
    		Priority_Prime(1);return 0;
    	}
    }
    namespace Boruvka
    {
    	int n,m,ans,fa[N],Vis[M],Min[M];
    	struct Line{int u,v,dis;} Lin[M];
    	inline int Find(int x) {return fa[x]==x?x:fa[x]=Find(fa[x]);}
    	inline bool Cmp(Line x,Line y) {return x.dis<y.dis;}
    	inline bool Check(int x,int y)
    	{
    		if(!y) return 1;
    		return Lin[x].dis!=Lin[y].dis?Lin[x].dis<Lin[y].dis:x<y;
    	}
    	inline void Boruvka()
    	{
    		int Jud=1;
    		for(int i=1;i<=n;i++) fa[i]=i;
    		while(Jud)
    		{
    			Jud=0;memset(Min,0,sizeof(Min));
    			for(int i=1;i<=m;i++)
    				if(!Vis[i]&&Find(Lin[i].u)!=Find(Lin[i].v))
    				{
    					int u=Find(Lin[i].u),v=Find(Lin[i].v);
    					if(Check(i,Min[u])) Min[u]=i;
    					if(Check(i,Min[v])) Min[v]=i;
    				}
    			for(int i=1;i<=n;i++)
    				if(Min[i]&&!Vis[Min[i]])
    				{
    					Jud=1;ans+=Lin[Min[i]].dis;Vis[Min[i]]=1;
    					fa[Find(Lin[Min[i]].u)]=Find(Lin[Min[i]].v);
    				}
    		}
    		printf("%d",ans);
    	}
    	inline int Main()
    	{
    		n=read(),m=read();
    		for(int i=1;i<=m;i++)
    			Lin[i].u=read(),Lin[i].v=read(),Lin[i].dis=read();
    		Boruvka();return 0;
    	}
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("1.in","r",stdin);
    #endif
    	//Kruskal::Main();
    	//Simple_Prime::Main();
    	//Priority_Prime::Main();
    	Boruvka::Main();
    }
    
    
  • 相关阅读:
    排序之选择排序
    排序之冒泡排序
    NOIP 模拟 $22; m d$
    NOIP 模拟 $20; m z$
    NOIP 模拟 $20; m y$
    NOIP 模拟 $20; m 玩具$
    NOIP 模拟 $21; m Median$
    NOIP 模拟 $21; m Park$
    NOIP 模拟 $21; m Game$
    NOIP 模拟 $19; m w$
  • 原文地址:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/11755202.html
Copyright © 2020-2023  润新知