• bzoj4890[Tjoi2017]城市(树的半径)


    4890: [Tjoi2017]城市

    Time Limit: 30 Sec  Memory Limit: 128 MB
    Submit: 149  Solved: 91
    [Submit][Status][Discuss]

    Description

     从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作。这个地区一共有ri座城市,《-1条高速公路,保证了任意两运城市之间都可以通过高速公路相互可达,但是通过一条高速公路需要收取一定的交通费用。小明对这个地区深入研究后,觉得这个地区的交通费用太贵。小明想彻底改造这个地区,但是由于上司给他的资源有限,因而小明现在只能对一条高速公路进行改造,改造的方式就是去掉一条高速公路,并且重新修建一条一样的高速公路(即交通费用一样),使得这个地区的两个城市之间的最大交通费用最小(即使得交通费用最大的两座城市之间的交通费用最小),并且保证修建完之后任意两座城市相互可达。如果你是小明,你怎么解决这个问题?

    Input

    输入数据的第一行为一个整数n,代表城市个数。

    接下来的n - 1行分别代表了最初的n-1条公路情况。每一行都有三个整数u,v,d。u,v代表这条公路的两端城市标号,d代表这条公路的交通费用。

    Output

     输出数据仅有一行,一个整数,表示进行了最优的改造之后,该地区两城市 之间最大交通费用。

    Sample Input

    5
    1 2 1
    2 3 2
    3 4 3
    4 5 4

    Sample Output

     7

    HINT

    对于30%的数据,1<=n<500

    对于100%的数据,1<=n<=5000

    1 <= u,v <= n,1<= d <= 2000

     

    /*
    要想删一条边再添一条边使一棵树中两点间最大距离最小
    容易想到跟树的直径有关
    N比较小,可以枚举要删那一条边。删掉(u,v)之后树就成了两个联通块。
    如果树的直径没变,对答案没有影响。 可以求出联通块1,2的直径。 
    (所以可以只枚举删直径上的边,但我没有...) 
    那么问题就转化为在1,2联通块内分别找一个点使它到联通块内最远的点距离最近。
    这个距离就是树的半径。然后把这两个点连起来即可。
    如何维护半径?
    求直径时需要求出每个点为起点的最长链和次长链 
    考虑距离它最远的那个点,在它的子树内还是子树外 
    子树内:最长链
    子树外:dfs维护这个点子树外的最长链
    如何维护一个点子树外的最长链?
    当dfs到一个x时,对于x和他的父亲y,若x不在y的最长链内,那么ans[x](x到子树外最远距离)为max(y最长链+dis[x][y],ans[y]+dis[x][y]) 
    否则ans[x]为max(y的次长链+dis[x][y],ans[y]+dis[x][y])
    最后对每种情况答案取min即可。 
    */
    #include<bits/stdc++.h>
    
    #define N 5007
    #define inf 0x3f3f3f3f
    
    using namespace std;
    int n,m,ans,cnt,dis,res;
    int head[N<<1],mv[N],u[N],v[N],w[N];
    int dp[N][2];
    bool vis[N];
    struct edge{
        int u,v,w,nxt;
    }e[N<<1];
    
    inline void add(int u,int v,int w)
    {
        e[++cnt].v=v;e[cnt].w=w;e[cnt].nxt=head[u];head[u]=cnt;
    }
    
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    void getd(int u)
    {
        for(int i=head[u];i;i=e[i].nxt)
        {
            int v=e[i].v;
            if(vis[v]) continue;
            vis[v]=1;getd(v);
            if(dp[u][0]<dp[v][0]+e[i].w)
            {
                dp[u][1]=dp[u][0];mv[u]=v;
                dp[u][0]=dp[v][0]+e[i].w;
            }
            else if(dp[u][1]<dp[v][0]+e[i].w)
                dp[u][1]=dp[v][0]+e[i].w;
        }dis=max(dis,dp[u][0]+dp[u][1]);
    }
    
    void getr(int u,int from)
    {
        res=min(res,max(from,dp[u][0]));
        for(int i=head[u];i;i=e[i].nxt)
        {
            int v=e[i].v;
            if(!vis[v]) continue;
            vis[v]=0;
            if(mv[u]==v) 
              getr(v,max(dp[u][1]+e[i].w,from+e[i].w));
            else 
              getr(v,max(dp[u][0]+e[i].w,from+e[i].w));
            
        }
    }
    
    void clear()
    {
        memset(dp,0,sizeof dp);
        memset(mv,0,sizeof mv);
        memset(vis,0,sizeof vis);
        res=inf;dis=0;
    }
    
    int main()
    {
        n=read();
        for(int i=1;i<n;i++)
        {
            u[i]=read();v[i]=read();w[i]=read();
            add(u[i],v[i],w[i]);add(v[i],u[i],w[i]);
        }
        int d1,d2,r1,r2;ans=res=inf;dis=0;
        for(int i=1;i<n;i++)
        {
            vis[v[i]]=1;getd(u[i]); d1=dis;
            dis=0;getd(v[i]); d2=dis;
            //联通块1,2的直径 
            vis[v[i]]=0;getr(u[i],0); r1=res;
            res=inf;getr(v[i],0); r2=res;
            //联通块1,2的半径 
            ans=min(ans,max(max(d1,d2),r1+r2+w[i]));
            clear();
        }
        printf("%d
    ",ans);
        return 0;
    }

     

  • 相关阅读:
    02Golang 初识包管理
    mongo append列表元素变 extend列表元素
    linux常用命令
    EA Desktop 修改安装路径
    离线部署MySQL5.6
    分析服务器日志命令
    在事件中使用useRoute、useRouter注意事项
    datasnap 指定 npm 打包文件目录
    css练手 tabs选项卡切换
    css 练手 下拉菜单
  • 原文地址:https://www.cnblogs.com/L-Memory/p/9820687.html
Copyright © 2020-2023  润新知