zoukankan      html  css  js  c++  java
  • 关于最小生成树


    最小生成树

    (By:Soroak)

    • 定义:一个有 (n) 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 (n) 个结点,并且有保持图连通的最少的边。最小生成树可以用 (kruskal) 算法或 (Prim) 算法求出。

    Kruskal

    • 定义: (Kruskal) 是基于贪心的思想得到的。
      首先我们把所有的边按照权值先从小到大排列,接着按照顺序选取每条边,如果这条边的两个端点不属于同一集合,那么就将它们合并,直到所有的点都属于同一个集合为止。看到这里,我们不难想到另外一个算法——并查集,说白了, (Kruskal) 算法就是基于并查集的贪心算法。

    • 时间复杂度: (O(MlogM)) (M) 是图中边的总数。

    • 基本思想: (Kruskal) 是以边为主导地位,始终选择当前可用的最小边权的边,每次选择边权最小的边连接的时候,要判断两个端点之间有没有联通。

    代码如下:(感谢gyh大佬的“赞助”)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    struct edge
    {
    	int u,v,w;//分存每一条边前,后坐标与权值 
    }a[3000010];
    
    int n,m,num;//存边的数量 
    int pre[1000010];//存并查集中的祖先 
    
    bool cmp(edge aa,edge bb)
    {
    	return aa.w<bb.w;
    }//结构体sort排序必须自定义排序函数 
    
    void add(int u,int v,int w)
    {
    	a[++num].u=u;
    	a[num].v=v;
    	a[num].w=w;
    }
    
    int find(int x)
    {
    	return pre[x]==x?x:pre[x]=find(pre[x]);
    }
    
    void join(int x,int y)//并集 
    {
    	int r1=find(x),r2=find(y);
    	if(r1!=r2) 
    	{
    		pre[r1]=r2;
    	}
    }
    
    signed main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++) 
    	{
    		pre[i]=i;//重置先祖 
    	}
    	for(int j=1;j<=m;j++)
    	{
    		int u,v,w;
    		scanf("%d%d%d",&u,&v,&w);//输入各边权值 
    		add(u,v,w);
    	}
    	sort(a+1,a+num+1,cmp);
    	int sum=0;
    	for(int i=1,tot=0;i<=num&&tot!=n;i++)
    	{
    		if(find(a[i].u)==find(a[i].v)) 
    		{
    			continue;
    		}
    		join(a[i].u,a[i].v);
    		++tot;
    		sum+=a[i].w;
    	}
    	printf("%d",sum);//输出 
    	return 0;
    }
    

    Prim

    • 定义: (Prim) 算法,图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点且其所有边的权值之和亦为最小。
    • 大致思想: (Prim)算法,是从点的方面考虑构建一颗最小生成树,而 (Kruskal) 算法则是从边的方面考虑构建一颗最小生成树。
    • 流程:
      • 将一个图分为两个部分,一部分为点集U,另一部分为点集V,U的初始集合为{V1},V的初始集合为{除V1之外的所有点}
      • 针对U开始寻找U中各节点所关联的边的权值最小的那个,然后将关联的点Vi加到U中,再把Vi从V中删除(注意:不能形成环!!!)
      • 递归执行步骤2,直到V中的集合为空
      • U中所有的点所构成的点就是这个图的最小生成树。

    代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    const int INF = 0x7ffff;//最大值 
    const int MARX = 1e5+10;
    using namespace std;
    //=============================================================
    struct edge
    {
    	int u,v,w,ne;//分存前点 后点 权重 
    }e[MARX<<2];
    struct p
    {
    	int num,diss;
    	bool operator < (const p &a) const
    	  {
    	  	return diss > a.diss;
    	  }
    }tmp;
    int head[MARX],dis[MARX];
    bool f[MARX]; 
    int num,n,m,s=1;
    //=============================================================
    void add(int u,int v,int w)//邻接表加入元素 
    {
    	e[++num].ne=head[u],head[u]=num;
    	e[num].u=u,e[num].v=v,e[num].w=w;
    }
    void dj(int s)
    {
    	priority_queue <p> q;
    	tmp.num=s,tmp.diss=0;q.push(tmp);
    	for(int i=1;i<=n;i++) dis[i]=INF;//赋极值 
    	dis[s]=0;//初始化 
    	for(int i=0;i<n && (!q.empty());)
    	{
    	  int top=q.top().num; q.pop();
    	  if(f[top]) continue;
    	  i++,f[top]=1;
    	  for(int j=head[top];j;j=e[j].ne)//找k点的临点,并进行比较 
    	    if(dis[e[j].v] > e[j].w && (!f[e[j].v]))
    		{
    		  dis[e[j].v] = e[j].w;
    		  tmp.num=e[j].v , tmp.diss=dis[e[j].v];
    		  q.push(tmp);
    		}
    	}
    }
    //=============================================================
    int main()
    {
    	scanf("%d%d",&n,&m);//输入
    	for(int i=1;i<=m;i++)
    	{
    	  int u,v,w;
    	  scanf("%d%d%d",&u,&v,&w);
    	  add(u,v,w);
    	  add(v,u,w);
    	}
    	dj(s);
    	int sum=0;
    	for(int i=1;i<=n;i++) sum+=dis[i];
    	printf("%d",sum);
    }
    
    /*
    //如题,此题为最小生成树prim模板
    //不再赘述 
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int MARX=2147483646;
    struct baka9
    {
    	int u,v,w,ne;
    }a[401000];
    int head[20010];
    int minn[20010];
    bool f[20010];
    int n,m,ans,num;
    void add(int x,int y,int z)
    {
    	a[++num].ne=head[x];
    	a[num].u=x;
    	a[num].v=y;
    	a[num].w=z;
    	head[x]=num;
    }
    bool prim();
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++)
    	  {
    	    int x,y,z; 
    		scanf("%d%d%d",&x,&y,&z);
    		add(x,y,z);
    		add(y,x,z);
    	  }
    	if( prim() )
     	  printf("%d",ans);
     	else
     	  printf("orz");
    }
    bool prim()
    {
    	for(int i=2;i<=n;i++)
    	  minn[i]=MARX;
    	for(int i=head[1];i;i=a[i].ne)
    	  minn[a[i].v]=min(minn[a[i].v],a[i].w);
    	for(int i=1;i<n;i++)
    	  {
    	  	int minnn=MARX,k=-1;
    	  	for(int j=1;j<=n;j++)
    	  	  if(!f[j] && minn[j]<minnn)
    	  	    {
    	  	  	  minnn=minn[j];
    	  	  	  k=j;
    			}
    		if(k==-1) break;
    		f[k]=1;
    		for(int l=head[k];l;l=a[l].ne)
    		  {
    		  	if(!f[a[l].v] && minn[a[l].v] > a[l].w)
    		  	  minn[a[l].v]=a[l].w;
    		  }
    	  }
    	for(int i=1;i<=n;i++)
    	  {
    	  	if(minn[i]==MARX)
    	  	  return 0;
    	  	ans+=minn[i];
    	  }
    	return 1;
    }
    */
    
    

    这里宣传一下gyh大佬的博客

  • 相关阅读:
    visual studio 注释
    EF Core导航属性
    【转】前端UI框架小汇总
    C#三种定时器的实现
    【转】自学MVC看这里——全网最全ASP.NET MVC 教程汇总
    【转】C#进阶系列——WebApi 接口参数不再困惑:传参详解
    【转】c# WebApi之解决跨域问题:Cors
    优秀.NET开源项目
    Linux简介及最常用命令
    C#中使用Socket实现简单Web服务器
  • 原文地址:https://www.cnblogs.com/Soroak/p/11700988.html
Copyright © 2011-2022 走看看