• 洛谷P1099&Noip 2007提高-树网的核(树直径上的尺取)


    题目链接:https://www.luogu.com.cn/problem/P1099
    CSDN食用链接:https://blog.csdn.net/qq_43906000/article/details/107874674

    题目描述

    (T=(V,E,W)) 是一个无圈且连通的无向图(也称为无根树),每条边到有正整数的权,我们称 (T) 为树网(treenetwork),其中 (V)(E) 分别表示结点与边的集合,(W) 表示各边长度的集合,并设 (T)(n) 个结点。

    路径:树网中任何两结点 (a)(b) 都存在唯一的一条简单路径,用 (d(a, b)) 表示以 (a, b) 为端点的路径的长度,它是该路径上各边长度之和。我们称 (d(a,b))(a, b) 两结点间的距离。

    (D(v, P)=min{d(v, u)}), (u) 为路径(P) 上的结点。

    树网的直径:树网中最长的路径成为树网的直径。对于给定的树网 (T),直径不一定是唯一的,但可以证明:各直径的中点(不一定恰好是某个结点,可能在某条边的内部)是唯一的,我们称该点为树网的中心。

    偏心距 (mathrm{ECC}(F)):树网 (T) 中距路径 (F) 最远的结点到路径 (F) 的距离,即

    (mathrm{ECC}(F)=max{d(v, F),v in V})

    任务:对于给定的树网 (T=(V, E, W))和非负整数 (s),求一个路径 (F),他是某直径上的一段路径(该路径两端均为树网中的结点),其长度不超过 ss(可以等于 (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)


    输入格式
    (n) 行。

    (1) 行,两个正整数 (n)(s),中间用一个空格隔开。其中 nn 为树网结点的个数,(s) 为树网的核的长度的上界。设结点编号以此为 (1,2dots,n)

    从第 (2) 行到第 (n) 行,每行给出 (3) 个用空格隔开的正整数 (u, v, w),依次表示每一条边的两个端点编号和长度。例如,2 4 7 表示连接结点 (2)(4) 的边的长度为 (7)

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

    输入输出样例
    输入
    5 2
    1 2 5
    2 3 2
    2 4 4
    2 5 3
    输出
    5

    输入
    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\%) 的数据,保证 (n le 15)
    对于 (70\%) 的数据,保证 (n le 80)
    对于 (100\%)的数据,保证 (n le 300,0le sle10^3,1 leq u, v leq n,1 leq w leq 10^3)

    emmm,这可能是为数不多的需要翻译的中文题。。。说点阳间的话就是:你需要在树的直径上找一段长为(s)的路径,使得距离(s)最远的的点距离(s)最近。至于点到路径的距离是个什么鬼?实际上也就是点到点的距离,如果s可以覆盖两个点,那么你就需要计算一下离这两个点最远的几个点的距离,然后取个最大值。如果只能覆盖半条边,那么它只能拿端点计算。

    那么当什么都不做的时候我们知道树的直径是最长的一条链,所以我们的s需要在这上面取来使得这个距离变小,那么在直径上取路径的话有两种情况,一种是直径的端点离这个路径最远,还有一种情况是非直径的点离这条路径最远。

    那么我们先来处理第一种情况,我们知道树的直径的做法可以用两遍dfs求,这样不仅求出了直径的两个端点,还求出了一个端点到所有点的距离(dist[v]),那么我们知道,对于一棵树而言,每个节点的父亲只有一个,那么也就是说我们可以根据(father)从直径的终点来推到直径的起点,既然已经知道了这条链了,那么我们就可以直接尺取了,(i,j)从终点开始,(j)一直往上爬,一旦(dis(i,j)>s)那么(i)就一直往上爬。同时在取的过程中我们取一下他们离直径两个端点最大值的最小值。其代码片段如下:

    void dfs(int x,int fa)
    {
    	father[x]=fa;
    	for (auto v:g[x]){
    		if (v.first==fa) continue;
    		dis[v.first]=dis[x]+v.second;
    		if (dis[v.first]>=d) {d=dis[v.first]; pt=v.first;}
    		dfs(v.first,x);
    	}
    }
    /*********/
    dfs(1,-1);
    memset(dis,0,sizeof dis);
    st=pt;
    dfs(pt,-1);
    ed=pt;
    int ans=inf;
    for (int i=ed,j=ed; (i!=-1) && (j!=-1); j=father[j]) {
    	while (dis[i]-dis[j]>s && i!=-1) i=father[i];
    	ans=min(ans,max(dis[j],dis[ed]-dis[i]));
    }
    

    接下来就是考虑第二种情况了,我们直接取不经过直径离直径上最远的点的最大值就好了,这个答案可以直接对直径上的每个点进行一次dfs就好了,不过在这之前由于不能经过直径,所以我们要对直径上的点打上标记:

    int dfs_dis(int x,int fa)
    {
    	int dist=0;
    	for (auto v:g[x]){
    		if (v.first==fa) continue;
    		if (vis[v.first]) continue;
    		dist=max(dist,dfs_dis(v.first,x)+v.second);
    	}
    	return dist;
    }
    /*************************/
    for (int i=ed; i!=-1; i=father[i]) vis[i]=1;
    for (int i=ed; i!=-1; i=father[i]) {
    	int p=dfs_dis(i,-1);
    	ans=max(p,ans);
    }
    

    于是此题就愉快地结束了!!

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    #define debug printf("@#$#@$2
    ")
    #define mk make_pair
    const int mac=1e3+10;
    const int inf=1e9+10;
    
    vector<pair<int,int> >g[mac];
    int dis[mac],d=0,pt,st,ed;
    int father[mac],vis[mac];
    
    void dfs(int x,int fa)
    {
    	father[x]=fa;
    	for (auto v:g[x]){
    		if (v.first==fa) continue;
    		dis[v.first]=dis[x]+v.second;
    		if (dis[v.first]>=d) {d=dis[v.first]; pt=v.first;}
    		dfs(v.first,x);
    	}
    }
    
    int dfs_dis(int x,int fa)
    {
    	int dist=0;
    	for (auto v:g[x]){
    		if (v.first==fa) continue;
    		if (vis[v.first]) continue;
    		dist=max(dist,dfs_dis(v.first,x)+v.second);
    	}
    	return dist;
    }
    
    int main(int argc, char const *argv[])
    {
    	int n,s;
    	scanf ("%d%d",&n,&s);
    	for (int i=1; i<n; i++){
    		int u,v,w;
    		scanf ("%d%d%d",&u,&v,&w);
    		g[u].push_back(mk(v,w)); g[v].push_back(mk(u,w));
    	}
    	dfs(1,-1);
    	memset(dis,0,sizeof dis);
    	st=pt;
    	dfs(pt,-1);
    	ed=pt;
    	int ans=inf;
    	for (int i=ed,j=ed; (i!=-1) && (j!=-1); j=father[j]){
    		while (dis[i]-dis[j]>s && i!=-1) i=father[i];
    		ans=min(ans,max(dis[j],dis[ed]-dis[i]));
    	}
    	for (int i=ed; i!=-1; i=father[i]) vis[i]=1;
    	for (int i=ed; i!=-1; i=father[i]){
    		int p=dfs_dis(i,-1);
    		ans=max(p,ans);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    JIT动态编译器的原理与实现之Interpreter(解释器)的实现(三)
    java工作之后需要看的书籍
    WebService 之 REST vs SOAP
    消息队列
    dreamweaver cs6 的破解方法
    jquery mobile页面跳转后js不执行的问题
    JQueryMobile页面跳转参数的传递解决方案
    HTMl5的sessionStorage和localStorage
    phoneGap、JQueryMobile 简介及中文API地址
    Android 禁止响应屏幕翻转
  • 原文地址:https://www.cnblogs.com/lonely-wind-/p/13456497.html
Copyright © 2020-2023  润新知