[TJOI2017]城市(树的直径)

Posted hhh1109

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[TJOI2017]城市(树的直径)相关的知识,希望对你有一定的参考价值。

[TJOI2017]城市

题目描述

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

输入输出格式

输入格式:

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

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

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

输出格式:

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

输入输出样例

输入样例#1: 复制

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

输出样例#1: 复制

7

说明

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

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



题解



友善的讲解一下.
断开一个点之后,我们只需要求出两个联通块的直径。
然后我们要保证断开的公路连接的两个联通块的半径最大值最小,这样能新生成的树的直径在这三个值中最小。
这个题目不难,难在我脑抽...直接照搬了[NOI2003]逃学的小孩的方法去写。但是我忘了其实点要在两个联通块的直径上才能求半径,不在直径上会影响半径的求取。
调了三天,看崩了几个人,常数极其大(可以优化但我懒得写了)。吸氧能过。



代码


#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=5001;
struct node{
    int to,nex,v;
}e[N<<1];
int num,head[N];
int dir[N],dis[N],r1,r2,s1,s2,t1,t2;
int n,m,ans=99999999,maxn1,maxn2;
int x[N],y[N],z[N],vis[N],ff[N];
int read(){
    int x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*w;
}

void add(int from,int to,int v){
    num++;
    e[num].to=to;
    e[num].v=v;
    e[num].nex=head[from];
    head[from]=num;
}

void dfs(int x,int fa){
    for(int i=head[x];i;i=e[i].nex){
        int v=e[i].to;if(v==fa)continue;ff[v]=x;
        dis[v]=dis[x]+e[i].v;dfs(v,x);
    }
}

void dfs2(int x,int fa){
    for(int i=head[x];i;i=e[i].nex){
        int v=e[i].to;if(v==fa)continue;
        dir[v]=dir[x]+e[i].v;dfs2(v,x);
    }
}

void clear(){
    memset(e,0,sizeof(e));memset(head,0,sizeof(head));num=0;
    r1=r2=maxn1=maxn2=0;
}

void solve(int now){
    for(int i=1;i<n;i++)
    if(i!=now)add(x[i],y[i],z[i]),add(y[i],x[i],z[i]);
    
    dfs(x[now],0);for(int i=1;i<=n;i++){if(dis[i]>maxn1)maxn1=dis[i],s1=i;ff[i]=dis[i]=0;}
    dfs(s1,0);maxn1=0;for(int i=1;i<=n;i++){if(dis[i]>maxn1)maxn1=dis[i],t1=i;}
    int tmp=t1;while(tmp){vis[tmp]=1;tmp=ff[tmp];}
    dfs2(t1,0);for(int i=1;i<=n;i++)
    {if(vis[i])r1=max(r1,min(dis[i],dir[i]));
    vis[i]=ff[i]=dis[i]=dir[i]=0;}
    
    dfs(y[now],0);for(int i=1;i<=n;i++){if(dis[i]>maxn2)maxn2=dis[i],s2=i;ff[i]=dis[i]=0;}  
    dfs(s2,0);maxn2=0;for(int i=1;i<=n;i++){if(dis[i]>maxn2)maxn2=dis[i],t2=i;}
    tmp=t2;while(tmp){vis[tmp]=1;tmp=ff[tmp];}
    dfs2(t2,0);for(int i=1;i<=n;i++){if(vis[i])r2=max(r2,min(dis[i],dir[i]));vis[i]=ff[i]=dis[i]=dir[i]=0;}
    ans=min(ans,max(maxn1,max(maxn2,maxn1+maxn2-r1-r2+z[now])));
    clear();
}

int main(){
//  freopen("data.in","r",stdin);
//  freopen("ans.out","w",stdout);
    n=read();
    for(int i=1;i<n;i++){
        x[i]=read(),y[i]=read(),z[i]=read();
    }
    for(int i=1;i<n;i++)
    solve(i);
    printf("%d
",ans);
    return 0;
}




以上是关于[TJOI2017]城市(树的直径)的主要内容,如果未能解决你的问题,请参考以下文章

luogu3761 [TJOI2017]城市

2017端午欢乐赛——Day1T3(树的直径+并查集)

P5536 XR-3核心城市(树的直径)

P5536 XR-3核心城市(树的直径)

大臣的旅费---树的直径(dfs)

51 nod 1427 文明 (并查集 + 树的直径)