zoukankan      html  css  js  c++  java
  • P3366 【模板】最小生成树

    原题链接 https://www.luogu.org/problemnew/show/P3366

    一道最小生成树的模板题......

    昨天刚学最小生成树,wz大佬讲的一塌糊涂井然有序,所以我们今天做起板子题来一脸懵逼游刃有余.....


    老师让wz大佬讲Prim算法,大佬竟然说不会.......于是给我们讲起了Kruskal算法,结果老师让我们用Prim算法解........

    话不多说,讲下Kruskal算法 ,要用到并查集 (且用到贪心思想)Prim算法被我吃辣,滑稽 

    对于任意一个连通网的最小生成树来说,在要求总的权值最小的情况下,最直接的想法就是将连通网中的所有边按照权值大小进行升序排序,从小到大依次选择。

    由于最小生成树本身是一棵生成树,所以需要时刻满足以下两点:

    • 生成树中任意顶点之间有且仅有一条通路,也就是说,生成树中不能存在回路
    • 对于具有 n 个顶点的连通网,其生成树中只能有 n-1 条边,这 n-1 条边连通着 n 个顶点。

    连接 n 个顶点在不产生回路的情况下,只需要 n-1 条边。

    所以克鲁斯卡尔算法的具体思路是:将所有边按照权值的大小进行升序排序,然后从小到大一一判断,条件为:如果这个边不会与之前选择的所有边组成回路,就可以作为最小生成树的一部分;反之,舍去。直到具有 n 个顶点的连通网筛选出来 n-1 条边为止。筛选出来的边和所有的顶点构成此连通网的最小生成树。

    判断是否会产生回路的方法为:在初始状态下给每个顶点赋予不同的标记,对于遍历过程的每条边,其都有两个顶点,判断这两个顶点的标记是否一致,如果一致,说明它们本身就处在一棵树中,如果继续连接就会产生回路;如果不一致,说明它们之间还没有任何关系,可以连接。

    假设遍历到一条由顶点 A 和 B 构成的边,而顶点 A 和顶点 B 标记不同,此时不仅需要将顶点 A 的标记更新为顶点 B 的标记,还需要更改所有和顶点 A 标记相同的顶点的标记,全部改为顶点 B 的标记。

    下面上代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    int n,m,fa[5001],tot=0,sum=0;      //tot记录最短路径,sum记录边数 
    struct point
    {
        int from;                      //一条边的始点 
        int to;                        //一条边的终点 
        int dis;                       //一条边的权值 
    }a[200001];
    int cmp(const point &x,const point &y)  //自定义排序,按权值大小排序,好进行下面的贪心算法 
    {
        return x.dis<y.dis;
    }
    int getf(int x)                                   //找父结点 
    {
        if(fa[x]!=x) fa[x]=getf(fa[x]);               //进一步找出父结点的父结点,也就是祖先结点 
        return fa[x];                                 //返回父结点 
    }
    void father(int x,int y)           
    { 
        int fx=getf(x);                              //找出x的父结点 
        int fy=getf(y);                              //找出y的父结点 
        if(fx!=fy) fa[fx]=fa[fy];                    //如果两个结点的父结点不同,则弄成相同的,证明在同一棵树上 
    }
    int main()
    { 
        cin>>n>>m;
        for(int i=1;i<=m;i++)                        //n条边 
        cin>>a[i].from>>a[i].to>>a[i].dis;
        for(int i=1;i<=n;i++)
        fa[i]=i;                                     //一开始每个结点都可以看作是一颗独立的树,那么该结点的父结点只能是它自己 
        sort(a+1,a+1+m,cmp);                         //自定义按照权值大小排序 
        for(int i=1;i<=m;i++)
        {
            if(getf(a[i].from)!=getf(a[i].to))       //如果父结点不同,也就是不在同一个树上,进而不能构成回环,则可以贪心计算上 
            {
                tot+=a[i].dis;                       //加上该边的权值 
                father(a[i].from,a[i].to);           //将两个结点标记为同一父结点 
                sum++;                               //边数+1 
            }
            if(sum==n-1) break;                      //对于n个结点的图,最小生成树只需要n-1条边就够了,如果边多了就不能保证是最优解了 
        }
        cout<<tot;                                   //输出最小生成树的值 
        return 0;                                     
    }

    完结撒花QAQ~

  • 相关阅读:
    uva 494 Kindergarten Counting Game
    uva 458
    Spring--quartz中cronExpression配置说明
    配置DTD提示的方法
    MySQL中怎么查询一张表的列数
    mysql 数据库的名称不能以数字开头
    Navicat: Can't create a procedure from within another stored routine
    解决JQUERY $符号的冲突
    如何截取iframe的内容,修改他的CSS
    struct框架
  • 原文地址:https://www.cnblogs.com/xcg123/p/10752581.html
Copyright © 2011-2022 走看看