• 图论(2)--论最小生成树


    图论(2)--论最小生成树

    树状数组??树...树.树呢?? --(zyx)

    上回书说到,简单的最小路问题,那么这次我们来谈谈最小生成树问题.

    最小生成树是指在固定的(n)个点,(m)条边的图中,找出(n-1)条边,使组成的图的边权最小且每个点之间可以相互到达(不一定是直接到达可以通过点的转移).

    我们知道由(n)个点,(n-1)条边组成的图不是一颗树吗??所以这就是我们所要探讨的话题--最小生成树的名字的来历.

    其实问题并不难,简单的来说,就是找(n-1)条边,使构成的图里没有环,且边权和最小.对于这个问题 我们提出两种方法也是最为普遍的两种方法

    Part 1 Prim算法

    利用一种很NB的思想--红黑树,

    思路

    设图的顶点集合为U,树的顶点集合为V

    从图中任意一点出发,找到N-1条边(x,y),x∈U,y∈V,且权值最小。

    通俗的讲,就是不断找权值最小不产生闭环N-1条边

    举例子

    话不多说,先上图

    (1)(V3)出发

    (2)找到边((V3,V1)),符合条件且最小,将(V1)加入(V)

    以此类推……

    (N)找到边((V2,V5)),符合条件且最小,将(V5)加入(V),最小生成树构造完成

    /*Prim核心代码*/
    for (int i=2;i<=n;i++)
        {
            lowcost[i]=a[i][1];//将与V1(或任意一点)有关的边存入lowcost(与各点最小权值)
        }
        for (int i=1;i<n;i++)
        {
            minval=1000000;//初始化最小值为正无穷
            for (int j=1;j<=n;j++)
            {
                    if (lowcost[j]>0&&lowcost[j]<minval)//如果当前权值不为0(即未连接过)且更小
                    {
                        k=j;//记录当前点
                        minval=lowcost[j];//将最小值存入
                    }
            }
            ans+=minval;//统计最小生成树最小权值和
            lowcost[k]=0;//标记该点
            for (int j=1;j<=n;j++)
            {
                    if (lowcost[j]>0&&lowcost[j]>a[k][j])//由于U集合点增加,需更新与各点最小权值边
                    {
                        lowcost[j]=a[k][j];
                    }
            }
        }
    

    Part 2 (Kruskal)算法

    这个算法就充分的利用了并查集.

    思路

    利用并查集的思想,在最初把所有的点的祖先定为自己,并将边按全职大小从小到大排序,遍历所有边,如果两端点的祖先不相同,则合并集合,(ans)加上边权的大小,同时计数器(++) 等到计数器(=n-1)的时候结束循环,则很容易得到答案;

    感性/理性证明

    不确定是哪一种的证明方式

    最小表示边权最小,所以我们只要排序后找前(n-1)条边,若有重边或存在环等不满足树的行为,则去掉这条边,加上第(n)条边即可,最终的答案一点是最小的.

    代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #define maxn 500005
    using namespace std;
    struct node{
    	int x,y,z;
    }edge[maxn];//从x->y 的边权为z的边 
    int fa[maxn],n,m,ans;
    bool operator <(node x,node y)
    {
    	return x.z<y.z;
    }
    int get(int x)
    {
    	if(x==fa[x]) return x;
    	return fa[x]=get(fa[x]);
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].z);
    	}
    	int cnt=1;
    	sort(edge+1,edge+m+1);
    	for(int i=1;i<=n;i++) fa[i]=i;
    	for(int i=1;i<=m;i++) 
    	{
    		if(cnt == n)
    		{
                printf("%d
    ",ans);
                return 0;
            }
    		int x=get(edge[i].x),y=get(edge[i].y);
    		if(x==y) continue;
    		fa[x]=y;
    		cnt+=1;
    		ans+=edge[i].z;
    	}
    	if(cnt!=n) printf("orz");
    	return 0;
    }
    
  • 相关阅读:
    Spring Boot从入门到精通(一)搭建第一个Spring Boot程序
    程序员未来的出路究竟在哪里?一位老码农的心声
    ​IntelliJ IDEA使用技巧—使用EasyCode插件一键生成代码04期
    浅谈Java后端开发工程师腾讯面试经历分享总结
    Java面试技巧—如何自我介绍
    互联网大厂Java面试题集—Spring boot常见面试题(二)
    互联网大厂Java面试题集—Spring boot面试题(一)
    ActiveMQ消息队列从入门到实践(4)—使用Spring JMS收发消息
    ActiveMQ消息队列从入门到实践(1)—JMS的概念和JMS消息模型
    有多少程序员干到35岁,那么其他人去干什么了?
  • 原文地址:https://www.cnblogs.com/--840-114/p/13412268.html
Copyright © 2020-2023  润新知