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

    最小生成树

    Kruskal

    我们考虑这样一件事情,那就是,求出一个联通图,使得所有边权之和最小。

    我们考虑这样一件事情,将所有边按照权值从小到大排序,每次取最小的,并且边的两端不连通的边连接。

    于是说,这棵生成树两点之间的路径中,边权最大值最小,且边权之和最小。

    证明详见 oi-wiki

    复杂度:

    • 暴力:(O(n^2+m))
    • 二叉堆:(O((n+m)log n))
    • ( ext{Fib}) 堆:(O(nlog n+m))

    Prim

    咕咕咕

    Boruvka

    咕咕咕

    非严格次小生成树

    枚举每一条边,倍增求生成树中, (u)(v) 的路径中非严格小于这条边的最大权值,计算如果替换后的生成树大小。

    对于每条边的答案取 (min)

    严格最小生成树

    同样倍增,求路径中严格小于新增边权的最大边,其余同上。

    (3k) 代码

    DAG 上的最小生成树

    题意:给定一张 DAG ,用多条以 (1) 开始的路径覆盖尽可能多的点,并且使路径长度之和最短。

    先用一遍 ( ext{dfs}) 求出可以到达的点的个数以及可能经过的所有边。

    考虑给 DAG 上的每个点赋值点权,使得每条边上边的起点不小于边的终点。

    将本来的 “以边权从小到大排序” 给为 “先以终点的点权从大到小排序,再以边权排序” 。

    这样可以保证生成树上每一条边都是合法的。

    例题:P2573 [SCOI2012]滑雪

    $ exttt{code}$
    #include<bits/stdc++.h>
    using namespace std;
    #define Maxn 100005
    #define Maxm 1000005
    typedef long long ll;
    inline int rd()
    {
    	 int x=0;
         char ch,t=0;
         while(!isdigit(ch = getchar())) t|=ch=='-';
         while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
         return x=t?-x:x;
    }
    int n,m,tot,cnt,sum;
    int h[Maxn],fa[Maxn];
    int hea[Maxn],nex[Maxm<<1],ver[Maxm<<1];
    ll edg[Maxm<<1],ans;
    bool vis[Maxn];
    void add(int x,int y,ll d){ ver[++tot]=y,nex[tot]=hea[x],hea[x]=tot,edg[tot]=d; }
    struct Data { int u,v; ll w; }edge[Maxm<<1];
    int Find(int x){ return (fa[x]==x)?x:(fa[x]=Find(fa[x])); }
    bool cmp(Data x,Data y) // 按照点权、边权排序
    {
    	 if(h[x.v]!=h[y.v]) return h[x.v]>h[y.v];
    	 return x.w<y.w;
    }
    void bfs() // 可以到达的点与边
    {
    	 queue<int> q;
    	 q.push(1),vis[1]=true;
    	 while(!q.empty())
    	 {
    	 	 int cur=q.front(); q.pop();
    	 	 for(int i=hea[cur];i;i=nex[i])
    	 	 {
    	 	 	 edge[++cnt]=(Data){cur,ver[i],edg[i]};
    	 	 	 if(!vis[ver[i]]) vis[ver[i]]=true,q.push(ver[i]);
    		 }
    	 }
    }
    int main()
    {
    	 n=rd(),m=rd();
    	 for(int i=1;i<=n;i++) h[i]=rd(),fa[i]=i;
    	 for(int i=1,x,y,d;i<=m;i++)
    	 {
    	 	 x=rd(),y=rd(),d=rd();
    	 	 if(h[x]>=h[y]) add(x,y,1ll*d);
    	 	 if(h[y]>=h[x]) add(y,x,1ll*d);
    	 }
    	 bfs();
    	 sort(edge+1,edge+cnt+1,cmp);
    	 for(int i=1,x,y;i<=cnt;i++)
    	 {
    	 	 x=Find(edge[i].u),y=Find(edge[i].v);
    	 	 if(x!=y) fa[x]=y,sum++,ans+=edge[i].w;
    	 }
    	 printf("%d %lld
    ",++sum,ans);
         return 0;
    }
    

    瓶颈生成树

    咕咕咕

    最小瓶颈路

    咕咕咕

    Kruskal 重构树

    可悲的 Kruskal 只能满足两点之间路径最大边权最小,

    贪婪的人们从不满足于弱小的 Kruskal,可悲的天才们沉迷于更为强大的结构,并残忍侵占了弱小的空间复杂度,将恶毒的眼光投向了可悲的时间复杂度。

    于是我们考虑这样一件事情,那就是,用 Kruskal 够构建出一棵更为厉害的树,考虑将它叫做 Kruskal 重构树。

    重构树将边权转移到点上,并每次连接两个连通块时都将两个连通块的顶部连到新建的点上。

    比如我们现在有这样一棵树:

    就可以生成这样一棵树:

    Kruskal 有一些优秀的性质:

    • 它是一棵二叉树,满足堆的性质。

    • 两个点的 (lca) 为这两个点的路径上的边权最小 (/) 大值。

    • 一个根节点以下的所有点都可以用边权不超过 (/) 少于根节点权值的边联通,也就是说,到点 (x) 的简单路径上最小边权最大值 (le val) 的所有点 (y) 均在 Kruskal 重构树上的某一棵子树内,且恰好为该子树的所有叶子节点。

    那么,根据这个性质,我们就可以快速解决 P4768 [NOI2018] 归程 啦!

  • 相关阅读:
    ionic2项目中实现md5加密
    ionic2中使用极光IM的WebSDK实现即时聊天
    react-native清除android项目缓存的命令
    在react-native项目中使用iconfont自定义图标库
    ionic2中使用videogular2实现m3u8文件播放
    vue-video-player集成videojs-contrib-hls实现.m3u8文件播放
    react组件生命周期
    在vue2中隐藏elementUI的tab栏
    Spark2.1.0——Spark初体验
    Spark2.1.0——运行环境准备
  • 原文地址:https://www.cnblogs.com/EricQian/p/15241924.html
Copyright © 2011-2022 走看看