[洛谷P3761][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


想法##

很显然,最优解中去掉的那条边一定在原先这棵树的直径上,否则交通费用最大的两个点之间距离没有缩短。
n这么小,可以枚举每条去掉的边。
去掉一条边后原图变为两棵树,将这两棵树连起来后,交通费用最大为max{第一棵树的直径,第二棵树的直径,两棵树的“重心”到最远结点的距离和+这条边的长度}
这里所说的树的“重心”指树中的一个结点,它到树中任意节点的距离最大值 最小

树的直径求法:
随便找一个点u,找到树中到它距离最远的点v
再找到树中到v距离最远的点w,v到w的距离即为树的直径。
(简单证明:假设树的直径为s-t 。首先对于s-t上任意一点,到它距离最远的点为s或t;对于不在s-t上的点u,它与到它距离最远的点之间的路径上一定与s-t有相交,到这个交点距离最远的点为s或t,则到u距离最远的点为s或t)

树的“重心”求法:
用两个数组mx1[]与mx2[],分别记录到某点u的最远距离 及 次远距离(次远距离所在路径与最远距离所在路径唯一交点为u
第一遍dfs,记录结点u到其子树中任意点的mx1[]与mx2[]
第二遍dfs,记录节点u到整棵树中任意点的mx1[]与mx2[]


代码##

(P.S.由于我太懒了,所以直接枚举去掉每一条边,不光是直径上的[捂脸])

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

#define INF 200000000

using namespace std;

const int N = 5005;

struct node{
    int v,len;
    node *next;
}pool[N<<1],*h[N];
int cnt;

void addedge(int u,int v,int len){
    node *p=&pool[++cnt],*q=&pool[++cnt];
    p->v=v;p->next=h[u];h[u]=p;p->len=len;
    q->v=u;q->next=h[v];h[v]=q;q->len=len;     
}

int dist[N],vis[N];
int dfs_len(int u,int vv){
    int v,w,ret=u;
    vis[u]=1; dist[u]=0;
    for(node *p=h[u];p;p=p->next)
        if(!vis[v=p->v] && v!=vv){
            w=dfs_len(v,vv);
            if(dist[u]<dist[v]+p->len){
                dist[u]=dist[v]+p->len;
                ret=w;
            }
        }
    vis[u]=0;
    return ret;
}
int diameter(int u,int vv){
    int v=dfs_len(u,vv);
    int w=dfs_len(v,vv);
    return dist[v];
}

int mx1[N],mx2[N];
void dfs1(int u,int vv){
    int v;
    vis[u]=1; 
    mx1[u]=mx2[u]=0;
    for(node *p=h[u];p;p=p->next)
        if(!vis[v=p->v] && v!=vv){
            dfs1(v,vv);
            if(mx1[u]<=mx1[v]+p->len){
                mx2[u]=mx1[u];
                mx1[u]=mx1[v]+p->len;     
            }
            else if(mx2[u]<mx1[v]+p->len)
                mx2[u]=mx1[v]+p->len;
        } 
    vis[u]=0;
}
int dfs2(int u,int vv){
    int v,w,ret;
    vis[u]=1;
    ret=mx1[u];
    for(node *p=h[u];p;p=p->next)
        if(!vis[v=p->v] && v!=vv){
            if(mx1[u]==mx1[v]+p->len)
                w=p->len+mx2[u];
            else w=p->len+mx1[u];
            if(w>=mx1[v])
                mx2[v]=mx1[v],mx1[v]=w;
            else if(w>mx2[v])
                mx2[v]=w;
            ret=min(ret,dfs2(v,vv));  
        }
    vis[u]=0;
    return ret;
}

int solve(int s,int t,int len){
    int ret=0,w=len;
    memset(vis,0,sizeof(vis));
    ret=max(diameter(s,t),diameter(t,s));
    dfs1(s,t); w+=dfs2(s,t); 
    dfs1(t,s); w+=dfs2(t,s); 
    return max(ret,w);
}

int fa[N];
int dfs(int u){
    int v,ret=INF;
    for(node *p=h[u];p;p=p->next)
        if(!fa[v=p->v]){
            ret=min(ret,solve(u,v,p->len));
            fa[v]=u;
            ret=min(ret,dfs(v));
        }
    return ret;
}
int n;

int main()
{
    int u,v,len;
    scanf("%d",&n);
    for(int i=1;i<n;i++)
        scanf("%d%d%d",&u,&v,&len),addedge(u,v,len);
    
    fa[1]=-1;
    printf("%d
",dfs(1));
    
    return 0;    
}
既然选择了远方,便只顾风雨兼程
原文地址:https://www.cnblogs.com/lindalee/p/8379317.html