<更新提示>

<第一次更新>


<正文>

树的直径

我们先来认识一下树的直径。

树是连通无环图,树上任意两点之间的路径是唯一的。定义树上任意两点\(u, v\)的距离为\(u\)到\(v\)路径上边权的和。树的直径\(MN\)为树上最长路径,即点\(M\)和\(N\)是树上距离最远的两个点,这条路径亦称为树的最长链。

那么,我们考虑一下如何求解树的直径。

方法一:\(DP\)求解树的直径。

设\(d_x\)表示从节点\(x\)出发走向以\(x\)为根的子树,能够达到的最远距离。

那么

\[d_x=\max_{y\in son(x)}\{d_y+e(x,y)\}
\]

(\(y\)为\(x\)的一个子节点,\(e(x,y)\)为从\(x\)到\(y\)的权值)

设\(f_x\)代表经过节点\(x\)的最长链的长度,则树的直径为\(\max_{1 \leq x \leq n}\{f_x\}\)。

考虑如何求解\(x\)。对于\(x\)的任意两个子节点\(y_i\)和\(y_j\),\(f_x\)由四部分组成,\(y_i\)到其子树中的最远距离,\(e(y_i,x)\),\(e(x,y_j)\),\(y_j\)到其子树中的最远距离。所以

\[f_x=\max_{y_i,y_j\in son(x)}\{d_{y_i}+d_{y_j}+e(y_i,x)+e(x,y_j)\}
\]

注意到\(d_{y_i}+e(y_i,x)\)和\(d_{y_j}+e(y_j,x)\)的格式的相同的,都是用于更新\(d_x\)的项,我们可以在枚举到一个新的\(y\)时利用上一个\(d_x\)的值顺带更\(f_x\),即用\(d_x+d_{y_i}+e(y_i,x)\),实现\(O(n)\)求解树的直径。

\(Code:\)

#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
#define mset(name,val) memset(name,val,sizeof name)
using namespace std;
const int N=40000+50;
int n,m,ans,vis[N],d[N],f[N];
struct edge{int val,ver;};
vector < edge > Link[N];
inline void input(void)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y,v;
scanf("%d%d%d",&x,&y,&v);
Link[x].push_back((edge){v,y});
Link[y].push_back((edge){v,x});
}
}
inline int dp(int x)
{
vis[x]=true;
for(int i=0;i<Link[x].size();i++)
{
int y=Link[x][i].ver;
if(!vis[y])
{
dp(y);
f[x]=max(f[x],d[x]+d[y]+Link[x][i].val);
d[x]=max(d[x],d[y]+Link[x][i].val);
}
}
ans=max(ans,f[x]);
}
int main(void)
{
input();
dp(1);
printf("%d\n",ans);
return 0;
}

方法二:两次\(BFS/DFS\)求解树的直径

①从树上任意一点\(P\)出发,找到距离它最远的一点\(M\)

②再从\(M\)出发,找到距离它最远的一点\(N\)

③\(MN\)即为树的直径

时间复杂度\(O(n)\)。

证明如下:

反证法:假设\(M\)不是直径的一个端点,\(AB\)是树的直径。

① 如果\(P\)是直径上的点,如图,\(PM > PB\)

则\(AP + PM > AP + PB = AB\)这与\(AB\)是直径矛盾。



② \(P\)到\(M\)路径与\(A\)到\(B\)路径有公共结点\(T\),如图

\(PT + TM > PT + TB\),则\(TM > TB\),故\(AT + TM > AT + TB = AB\),矛盾。



③ \(P\)到\(M\)的路径与\(A\)到\(B\)的路径无公共结点,如图

\(PC + CM > PC + CD + BD\),则\(CM > CD + BD,CM + CD > BD\)

故\(CM + CD + AD > BD + AD = AB\),矛盾。

\(Code:\)

#include<cstdio>
#include<iostream>
#include<queue>
#include<vector>
#include<cstring>
#define mset(name,val) memset(name,val,sizeof name)
using namespace std;
const int N=40000+50;
int n,m,ans,vis[N],dis[N];
struct edge{int val,ver;};
vector < edge > Link[N];
inline void input(void)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y,v;
scanf("%d%d%d",&x,&y,&v);
Link[x].push_back((edge){v,y});
Link[y].push_back((edge){v,x});
}
}
inline int Search(int start)
{
queue< int >q;
mset(vis,0x00);
mset(dis,0x00);
vis[start]=1;
q.push(start);
while(!q.empty())
{
int temp=q.front();q.pop();
for(int i=0;i<Link[temp].size();i++)
{
if(!vis[Link[temp][i].ver])
{
vis[Link[temp][i].ver]=true;
dis[Link[temp][i].ver]=dis[temp]+Link[temp][i].val;
q.push(Link[temp][i].ver);
}
}
}
int res=0,Maxdis=0;
for(int i=1;i<=n;i++)
{
if(dis[i]>Maxdis)
{
Maxdis=dis[i];
res=i;
}
}
return res;
}
int main(void)
{
input();
int p=Search(1);
printf("%d\n",dis[Search(p)]);
return 0;
}

我们通过一道例题详细地了解一下。

Two(POJ1849)

Description

The city consists of intersections and streets that connect them.

Heavy snow covered the city so the mayor Milan gave to the winter-service a list of streets that have to be cleaned of snow. These streets are chosen such that the number of streets is as small as possible but still every two intersections to be connected i.e. between every two intersections there will be exactly one path. The winter service consists of two snow plovers and two drivers, Mirko and Slavko, and their starting position is on one of the intersections.

The snow plover burns one liter of fuel per meter (even if it is driving through a street that has already been cleared of snow) and it has to clean all streets from the list in such order so the total fuel spent is minimal. When all the streets are cleared of snow, the snow plovers are parked on the last intersection they visited. Mirko and Slavko don’t have to finish their plowing on the same intersection.

Write a program that calculates the total amount of fuel that the snow plovers will spend.

Input Format

The first line of the input contains two integers: N and S, 1 <= N <= 100000, 1 <= S <= N. N is the total number of intersections; S is ordinal number of the snow plovers starting intersection. Intersections are marked with numbers 1...N.

Each of the next N-1 lines contains three integers: A, B and C, meaning that intersections A and B are directly connected by a street and that street's length is C meters, 1 <= C <= 1000.

Output Format

Write to the output the minimal amount of fuel needed to clean all streets.

Sample Input

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

Sample Output

6

解析

题目大意就是有一棵树, 在s结点放两个机器人, 这两个机器人会把树的每条边都走一遍, 但是最后机器人不要求回到出发点。问你两个机器人走的路总长之和的最小值是多少?

首先,我们假设只有一个机器人,那么答案是什么?

我们可以让机器人沿着从起点开始的某一条最远距离路径走,对于路径上的其他子树,机器人需要进入遍历,并返回,需要花费两倍的子树权值和,但由于机器人不需要回到起点,所以答案为\(2*\sum w_i-d\),\(d\)为出发点所能到达的最远距离。同理,如果有两个机器人,那么我们就让他们分别向两条不同的路径走去,这样就正好对应了树的直径的\(BFS/DFS\)求法,机器人走的路径就成了树的直径,那么最终的答案就是\(2*\sum w_i-D\),\(D\)为树的直径长度。

\(Code:\)

#include<cstdio>
#include<iostream>
#include<queue>
#include<vector>
#include<cstring>
#define mset(name,val) memset(name,val,sizeof name)
using namespace std;
const int N=100000+50;
int n,s,sum,vis[N],dis[N];
struct edge{int val,ver;};
vector < edge > Link[N];
inline void input(void)
{
scanf("%d%d",&n,&s);
for(int i=1;i<n;i++)
{
int x,y,v;
scanf("%d%d%d",&x,&y,&v);
Link[x].push_back((edge){v,y});
Link[y].push_back((edge){v,x});
sum+=v*2;
}
}
inline int Search(int start)
{
queue< int >q;
mset(vis,0x00);
mset(dis,0x00);
vis[start]=1;
q.push(start);
while(!q.empty())
{
int temp=q.front();q.pop();
for(int i=0;i<Link[temp].size();i++)
{
if(!vis[Link[temp][i].ver])
{
vis[Link[temp][i].ver]=true;
dis[Link[temp][i].ver]=dis[temp]+Link[temp][i].val;
q.push(Link[temp][i].ver);
}
}
}
int res=0,Maxdis=0;
for(int i=1;i<=n;i++)
{
if(dis[i]>Maxdis)
{
Maxdis=dis[i];
res=i;
}
}
return res;
}
int main(void)
{
input();
int p=Search(s);
printf("%d\n",sum-dis[Search(p)]);
return 0;
}

<后记>

『Two 树的直径求解及其运用』的更多相关文章

  1. computer(树形dp || 树的直径)

    Computer Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  2. poj 1985 Cow Marathon 树的直径

    题目链接:http://poj.org/problem?id=1985 After hearing about the epidemic of obesity in the USA, Farmer J ...

  3. POJ 1985 Cow Marathon && POJ 1849 Two(树的直径)

    树的直径:树上的最长简单路径. 求解的方法是bfs或者dfs.先找任意一点,bfs或者dfs找出离他最远的那个点,那么这个点一定是该树直径的一个端点,记录下该端点,继续bfs或者dfs出来离他最远的一 ...

  4. POJ 1985 Cow Marathon (模板题)(树的直径)

    <题目链接> 题目大意: 给定一颗树,求出树的直径. 解题分析:树的直径模板题,以下程序分别用树形DP和两次BFS来求解. 树形DP: #include <cstdio> #i ...

  5. POJ 2631 Roads in the North (模板题)(树的直径)

    <题目链接> 题目大意:求一颗带权树上任意两点的最远路径长度. 解题分析: 裸的树的直径,可由树形DP和DFS.BFS求解,下面介绍的是BFS解法. 在树上跑两遍BFS即可,第一遍BFS以 ...

  6. 与图论的邂逅01:树的直径&基环树&单调队列

    树的直径 定义:树中最远的两个节点之间的距离被称为树的直径.  怎么求呢?有两种官方的算法(不要问官方指谁我也不晓得): 1.两次搜索.首先任选一个点,从它开始搜索,找到离它最远的节点x.然后从x开始 ...

  7. D4 树的直径、重心以及基环树

    第一题第二题鉴上我前几篇博客poj1985 poj1849:https://www.cnblogs.com/Tyouchie/p/10384379.html 第三题:数的重心:poj1655 来自sj ...

  8. 【UOJ #351】新年的叶子(树的直径,期望)

    题目链接 这的确是一道好题,我们不妨依循思路一步步推导,看问题是如何被解决的. 做一些约定,设$m$为树的叶子节点个数,设$len$为该树的直径(经过的点数). 毫无疑问,直径可能有多条,我们需要把所 ...

  9. 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分

    树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...

随机推荐

  1. Linux安装Tomcat-Nginx-FastDFS-Redis-Solr-集群——【第六集之补充:文本编辑器vi/vim】

    一:vi/vim的基本使用流程,掌握这三个步骤就算是入门vi或者vim.接下来的学习都是对vim命令和使用技巧的掌握,这要求各位自己去记忆.因为很少使用到某些命令,自然我们经常忘记这些命令,所以一旦忘 ...

  2. FFT快速傅里叶变换算法

    1.FFT算法概要: FFT(Fast Fourier Transformation)是离散傅氏变换(DFT)的快速算法.即为快速傅氏变换.它是根据离散傅氏变换的奇.偶.虚.实等特性,对离散傅立叶变换 ...

  3. macof python攻击脚本

    #!/usr/bin/python import sys from scapy.all import * import time iface="eth0" if len(sys.a ...

  4. 【Linux】如何在Linux上安装使用SSH

    SSH是什么? Secure Shell 安全外壳协议 建立在应用层基础上的安全协议 可靠,专为远程登录会话和其他网络服务提供安全性的协议 有效防止远程管理过程中的信息泄露问题 SSH客户端适用于多种 ...

  5. 创建线程的一般方式和匿名内部类方式对比——实现runnable接口,重新run方法

    启动:使用静态代理设计模式 优点:可同时实现继承,避免单继承局限性 一般方式: Programer.java /** * 真实角色 * * @author :liuqi * @date :2018-0 ...

  6. python3.5.2库getpass

    getpass的功能是:允许隐式的输入字符串 import getpass _username='vigossr' _password='haha' username=input('username: ...

  7. 调用获取学生信息的接口,保存到excel里面

    # 2.http: // doc.nnzhp.cn / index.php?s = / 6 & page_id = 14# 调用获取学生信息的接口,保存到excel里面 import requ ...

  8. 关于Python2 与 Python3 的区别

    Python是一门动态解释性的强类型定义语言. 1.Python2 : ①.臃肿,源代码的重复量很多.   ②.语法不清晰,掺杂着C,php,Java的一些陋习. Python3 : 几乎是重构后的源 ...

  9. 蓝鲸单机安装mysql问题记录

    1.注意默认启动用户为mysql 2.由于执行指令用的root,有些文件会生成为root用户 3.注意将datadir basedir  socket dir  修改称mysql  chown mys ...

  10. 如何设置body高度为浏览器高度

    html{height:100%} body{min-height:100%} 有时我们的页面上内容不多,但设计师要求背景色必须铺满全屏,这时候只需在样式表中加上这行,body就以浏览器的高度显示,超 ...