zoukankan      html  css  js  c++  java
  • 构造完全图 Codgic1352 最小完全图 Codevs2796

    【问题描述】 对于完全图G,若有且仅有一棵最小生成树为T,则称完全图G是树T的扩展出的。给你一 棵树T,找出T能扩展出的边权和最小的完全图G。

    【文件输入】 第一行N表示树T的点数。 接下来N-1行:Si,Ti,Di;描述一条边(Si,Ti)权值为 Di。 保证输入数据构成一棵树。

    【文件输出】 一个数,表示最小的图G的边权和。

    【样例输入】 4 1 2 1 1 3 1 1 4 2

    【样例输出】 12

    【样例说明】 添加D(2,3)=2,D(3,4)=3,D(2,4)=3即可。

    【数据范围】 对于20%的数据,N<=10 对于50%的数据,N<=1000 对于100%的数据,N<=100000,1<=Di<=100000

    首先,作为一名OIer,看到最小生成树,脑海中一定会浮现Prim算法和Kruskal算法。

    我们可以从Prim算法生成最小生成树的过程中获得灵感:

    对于一个图的最小生成树中的每一条边 E<s,t>,都有:E在与s相连的所有边中权值最小,且E在与t相连的所有边中权值最小。

    这个结论同样适用于本题的完全图G。

    由于本题给出的是一个最小生成树T,所以我们可以知道,T中的每一条边都是一条割边。

    去掉任意一条树T中的边,这个树一定会变成两个联通块,令其为A、B。要将T扩展为完全图G,那么显然 A中的每个点都需要与B中的所有点相连。

    由之前推出的结论可得,A中新发出的连边的权值一定是大于(若等于则存在多种最小生成树解,不合题意)发出点在树上的相接边的权值的。

    再一看数据量,单独枚举肯定不行。由联通块可以联想到并查集,发现可行。

    对于一条最小生成树上的边E<A,B>,可以看做E连接了A,B两个联通块,那么要将A、B两块连接为一个完全图需要加的边数就是 cnt[A]*cnt[B]-1,其中cnt表示联通块中的结点个数。

    由结论可得,这些边的权值一定是大于E的权值的,要使其最小,那么这些边的权值都是 E的权值+1

    那么合并A、B两个联通块的花费就是 (边E.权+1)*(cnt[A]*cnt[B]-1)。ans在每次合并联通块时加上花费即可求解。

    另外一点,因为要求的是最小的完全图,所以有一个贪心策略:先把树上的边按照权值从小到大排序,然后枚举即可。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #include<cmath>
     5 #include<string>
     6 using namespace std;
     7 template<class T> inline void read(T &_a){
     8     bool f=0;int _ch=getchar();_a=0;
     9     while(_ch<'0' || _ch>'9'){if(_ch=='-')f=1;_ch=getchar();}
    10     while(_ch>='0' && _ch<='9'){_a=(_a<<1)+(_a<<3)+_ch-'0';_ch=getchar();}
    11     if(f)_a=-_a;
    12 }
    13 
    14 const int maxn=100001;
    15 struct edge
    16 {
    17     int from,to,next; long long dis;
    18     bool operator < (const edge x) const {return dis<x.dis;}
    19 }w[maxn];
    20 int n,fa[maxn];
    21 long long ans,cnt[maxn];
    22 
    23 int find(int u)
    24 {
    25     return fa[u]==u?u:fa[u]=find(fa[u]);
    26 }
    27 
    28 int main()
    29 {
    30     freopen("tree.in","r",stdin);
    31     freopen("tree.out","w",stdout);
    32     read(n);
    33     for (register int i=1;i<n;++i) read(w[i].from),read(w[i].to),read(w[i].dis),ans+=w[i].dis;
    34     for (register int i=1;i<=n;++i) fa[i]=i,cnt[i]=1;
    35     sort(w+1,w+n);
    36     for (register int i=1;i<n;++i)
    37     {
    38         int a1=find(w[i].from);
    39         int a2=find(w[i].to);
    40         ans+=(w[i].dis+1)*(cnt[a1]*cnt[a2]-1);
    41         fa[a1]=a2;
    42         cnt[a2]+=cnt[a1];
    43     }
    44     printf("%lld",ans);
    45     return 0;
    46 }
    View Code
  • 相关阅读:
    自我介绍
    最大连通子数组求和
    敏捷开发方法综述
    第四周学习进度条
    时间日志和缺陷日志
    最大子数组2.0
    最大子数组1.0
    第三周学习进度条
    小学四则运算3.0
    单元测试
  • 原文地址:https://www.cnblogs.com/jaywang/p/7692133.html
Copyright © 2011-2022 走看看