牛客网:树网的核(DFS)

链接:https://ac.nowcoder.com/acm/problem/16646
来源:牛客网
 

题目描述

T=(V, E, W) 是一个无圈且连通的无向图(也称为无根树),每条边带有正整数的权,我们称T为树网(treenetwork),其中V, E分别表示结点与边的集合,W表示各边长度的集合,并设T有n个结点。
路径:树网中任何两结点a,b都存在唯一的一条简单路径,用d(a,b)表示以a,b为端点的路径的长度,它是该路径上各边长度之和。我们称d(a,b)a,b两结点间的距离。
一点v到一条路径P的距离为该点与P上的最近的结点的距离:
d(v,P) = min{d(v,u)u为路径P上的结点}。
树网的直径:树网中最长的路径称为树网的直径。对于给定的树网T,直径不一定是唯一的,但可以证明:各直径的中点(不一定恰好是某个结点,可能在某条边的内部)是唯一的,我们称该点为树网的中心。
偏心距ECC(F):树网T中距路径F最远的结点到路径F的距离,即ECC(F)=min{d(v,F),v∈V}。
任务:对于给定的树网T=(V, E,W)和非负整数s,求一个路径F,它是某直径上的一段路径(该路径两端均为树网中的结点),其长度不超过s(可以等于s),使偏心距ECC(F)最小。我们称这个路径为树网T=(V,E,W)(Core)。必要时,F可以退化为某个结点。一般来说,在上述定义下,核不一定只有一个,但最小偏心距是唯一的。
下面的图给出了树网的一个实例。图中,A-B与A-C是两条直径,长度均为20。点W是树网的中心,EF边的长度为5。如果指定s=11,则树网的核为路径DEFG(也可以取为路径DEF),偏心距为8。如果指定s=0(或s=1、s=2),则树网的核为结点F,偏心距为12。

输入描述:

第1行,两个正整数n和s,中间用一个空格隔开。其中n为树网结点的个数,s为树网的核的长度的上界。设结点编号依次为1, 2, ..., n。
从第2行到第n行,每行给出3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“2 4 7”表示连接结点2与4的边的长度为7。
所给的数据都是正确的,不必检验。

输出描述:

输出一个非负整数,为指定意义下的最小偏心距。

示例1

输入

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

输出

5

示例2

输入

8 6
1 3 2
2 3 2
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3

输出

5

备注:

40%的数据满足:5 ≤ n ≤ 15
70%的数据满足:5 ≤ n ≤ 80
100%的数据满足:5 ≤ n ≤ 300, 0 ≤ s ≤ 1000。边长度为不超过1000的正整数

思路:我们可以任意枚举一个起点,找到一条刚好路径和不大于s的路径,将该路径作为树网的核求当前最小值,我们并没有判断这个路径是否在直径上,因为只有路径在直径上时才有可能出现最优解!

import java.util.*;
public class Main{
    
    private static int s,ans,n;
    private static int[] depth;
    private static boolean[] vis;
    private static List<List<int[]>> edges;
    
    public static void main(String[] args){
        int x,y,z;
        ans=Integer.MAX_VALUE;
        edges=new ArrayList<>();
        Scanner in=new Scanner(System.in);
        n=in.nextInt();
        s=in.nextInt();
        depth=new int[n+1];
        vis=new boolean[n+1];
        for(int i=0;i<=n;i++)
            edges.add(new ArrayList<>());
        for(int i=1;i<n;i++){
            x=in.nextInt();
            y=in.nextInt();
            z=in.nextInt();
            edges.get(x).add(new int[]{y,z});
            edges.get(y).add(new int[]{x,z});
        }
        for(int i=1;i<=n;i++)
            dfs(i,0,0,new LinkedList<>(),new LinkedList<>());
        System.out.println(ans);
    }
    private static void dfs(int u,int f,int dep,Deque<Integer> q,Deque<Integer> q1){
        vis[u]=true;
        int size=edges.get(u).size();
        for(int i=0;i<size;i++){
            int[] nxt=edges.get(u).get(i);
            if(nxt[0]==f || nxt[1]>s) continue;
            if(dep+nxt[1]<=s){
                q1.add(nxt[0]);
                q.addLast(nxt[1]);
                dfs(nxt[0],u,dep+nxt[1],q,q1);
                q.pollLast();
            }else
                continue;
            vis[nxt[0]]=false;
        }
        findAns();
        vis[u]=false;
    }
    
    private static void findAns(){
        Arrays.fill(depth,10000000);
        for(int i=1;i<=n;i++){
            if(!vis[i]) 
                continue;
            depth[i]=0;
            dfs1(i,0);
        }
        int res=0;
        for(int i=1;i<=n;i++)
            res=Math.max(res,depth[i]);
        ans=Math.min(ans,res);
    }
    
    private static void dfs1(int u,int f){
        int size=edges.get(u).size();
        for(int i=0;i<size;i++){
            int[] v=edges.get(u).get(i);
            if(v[0]==f || vis[v[0]]) continue;
            depth[v[0]]=Math.min(depth[v[0]],depth[u]+v[1]);
            dfs1(v[0],u);
        }
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值