• Problem A: 道路建设 解题报告



    • 一定存在一个最优解是一条链

      否则可以接上去,不会更差

    • 边权最小的边一定在这条链上

      这个比较显然

      可以把所有边都减去这个最后加上就行了

    • 把链上的边按距离当前根的深度从小到大排列,设第一个零边位置为(k),那么到(k-2)及之前所有的边边权非严格递减,这个可以手玩一下。

      具体思路就是如果不递减,就可以强行把0边一个端点接过来,换掉(k-1),可以证明这样不会变差。

      于是边权可以直接累加起来了。

    • 建议一个虚点,连接所有的点,边权为被连接点出边最小边权的两倍。

      两倍是模拟把0边拉过来耗掉的那条边,然后如果拉的恰好是0边,计算的时候也没有影响。

      其余边照连,从虚点跑单源最短路即可。

    • 为什么跑出的最短路树边权递增?

      特殊点在于先连接的那条最短边,反证一下即可。


    Code:

    #include <cstdio>
    #define ll long long
    const int N=2010;
    int n,g[N][N];
    ll mi=(1ll<<50),dis[N],vis[N];
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            for(int j=i+1;j<=n;j++)
            {
                scanf("%d",&g[i][j]);
                mi=mi<g[i][j]?mi:g[i][j];
                g[j][i]=g[i][j];
            }
        for(int i=1;i<=n;i++)
        {
            int id=0;
            for(int j=1;j<=n;j++)
                if(i!=j)
                {
                    g[i][j]-=mi;
                    if(!id||g[i][j]<g[i][id]) id=j;
                }
            dis[i]=2ll*g[i][id];
        }
        for(int i=1;i<=n;i++)
        {
            int id=0;
            for(int j=1;j<=n;j++) if(!vis[j]&&(!id||dis[j]<dis[id])) id=j;
            vis[id]=1;
            for(int j=1;j<=n;j++) dis[j]=dis[j]<dis[id]+g[id][j]?dis[j]:dis[id]+g[id][j];
        }
        for(int i=1;i<=n;i++) printf("%lld
    ",dis[i]+1ll*(n-1)*mi);
        return 0;
    }
    

    2019.1.2

  • 相关阅读:
    VMware 中安装Centos
    go with go
    [APIO2012]派遣【左偏树】
    后缀自动机三·重复旋律6
    AC自动机(二次加强版)
    [HAOI2016]找相同字符【GSAM广义后缀自动机】
    吉司机线段树【学习笔记】
    [ZJOI2015]诸神眷顾的幻想乡【GSAM】
    广义后缀自动机(广义 SAM)【模板】
    Important Sisters【HDU-4694】【Dominator Tree】
  • 原文地址:https://www.cnblogs.com/butterflydew/p/10208827.html
Copyright © 2020-2023  润新知