hdu3721 Building Roads

21 篇文章 0 订阅
9 篇文章 0 订阅

题目在这里呀~

题意

给你一棵树,改变一条边使得树的直径最小,求最小直径。

题解

这题在考试的题目有加强版然而暴力都没有写完qaq所以找到了这道题。
这题还是挺友善的嗷(Case x忘记写了也是很迷了
枚举删除哪一条边,把树分成了两棵树,然后加的那条边一定是两棵树中心的连线。(一棵树的中心到其他节点最深的深度最小)所以只要用树形DP求中心即可。最后整棵树的直径是两棵树的直径以及两棵树中心到其他节点的对应深度加上边的长度。

//Suplex
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 3000
using namespace std;
const int inf=1e9;
int hxy,T,n,cnt,ans1,ans2,ans,p1,p2,max1,max2,vet[N+N],Next[N+N],head[N],len[N+N];
int d1[N],d2[N],c1[N],g[N],f[N],vis[N];

struct edge{
    int u,v,l;
}e[N+N];

inline void add_edge(int u,int v,int l)
{
    vet[++cnt]=v;Next[cnt]=head[u];len[cnt]=l;head[u]=cnt;
}

void dfs1(int u,int fa)
{
    vis[u]=1;
    for(int i=head[u];i!=-1;i=Next[i]){
        int v=vet[i];
        if(v==fa) continue;
        dfs1(v,u);
        if(d1[v]+len[i] > d1[u]){
            d2[u]=d1[u];
            d1[u]=d1[v]+len[i];
            c1[u]=v;
        }else if(d1[v]+len[i] > d2[u]) d2[u]=d1[v]+len[i];
    }
}

void dfs2(int u,int fa)
{
    for(int i=head[u];i!=-1;i=Next[i]){
        int v=vet[i];
        if(v==fa) continue;
        if(v==c1[u]) g[v]=max(g[u],d2[u])+len[i];
        else g[v]=max(g[u],d1[u])+len[i];
        dfs2(v,u);
    }
}

int main()
{
    scanf("%d",&T);
    while(T--){
        hxy++;
        scanf("%d",&n);
        for(int i=1;i<n;i++) scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].l);
        ans=inf;
        for(int i=1;i<n;i++){
            memset(d1,0,sizeof(d1));
            memset(d2,0,sizeof(d2));
            memset(c1,0,sizeof(c1));
            memset(g,0,sizeof(g));
            memset(f,0,sizeof(f));
            memset(head,-1,sizeof(head));
            cnt=0;ans1=inf;ans2=inf;max1=0;max2=0;
            for(int j=1;j<n;j++)
                if(i!=j) add_edge(e[j].u,e[j].v,e[j].l),add_edge(e[j].v,e[j].u,e[j].l);

            memset(vis,0,sizeof(vis));
            dfs1(e[i].u,-1);
            dfs2(e[i].u,-1);
            for(int j=0;j<n;j++)
                if(vis[j]) f[j]=max(g[j],d1[j]);
            for(int j=0;j<n;j++)
                if(vis[j])
                    if(f[j]<ans1){ans1=f[j];p1=j;}
            for(int j=0;j<n;j++)
                if(vis[j]) max1=max(max1,max(g[j],d2[j])+d1[j]);

            memset(vis,0,sizeof(vis));
            dfs1(e[i].v,-1);
            dfs2(e[i].v,-1);
            for(int j=0;j<n;j++)
                if(vis[j]) f[j]=max(d1[j],g[j]);
            for(int j=0;j<n;j++)
                if(vis[j])
                    if(f[j]<ans2){ans2=f[j];p2=j;}
            for(int j=0;j<n;j++)
                if(vis[j]) max2=max(max2,max(g[j],d2[j])+d1[j]);
            
            ans=min(ans,max(max(max1,max2),ans1+ans2+e[i].l));
        }
        printf("Case %d: %d\n",hxy,ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值