『Two 树的直径求解及其运用』
<更新提示>
<第一次更新>
<正文>
树的直径
我们先来认识一下树的直径。
树是连通无环图,树上任意两点之间的路径是唯一的。定义树上任意两点\(u, v\)的距离为\(u\)到\(v\)路径上边权的和。树的直径\(MN\)为树上最长路径,即点\(M\)和\(N\)是树上距离最远的两个点,这条路径亦称为树的最长链。
那么,我们考虑一下如何求解树的直径。
方法一:\(DP\)求解树的直径。
设\(d_x\)表示从节点\(x\)出发走向以\(x\)为根的子树,能够达到的最远距离。
那么
\]
(\(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\)到其子树中的最远距离。所以
\]
注意到\(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 树的直径求解及其运用』的更多相关文章
- computer(树形dp || 树的直径)
Computer Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- poj 1985 Cow Marathon 树的直径
题目链接:http://poj.org/problem?id=1985 After hearing about the epidemic of obesity in the USA, Farmer J ...
- POJ 1985 Cow Marathon && POJ 1849 Two(树的直径)
树的直径:树上的最长简单路径. 求解的方法是bfs或者dfs.先找任意一点,bfs或者dfs找出离他最远的那个点,那么这个点一定是该树直径的一个端点,记录下该端点,继续bfs或者dfs出来离他最远的一 ...
- POJ 1985 Cow Marathon (模板题)(树的直径)
<题目链接> 题目大意: 给定一颗树,求出树的直径. 解题分析:树的直径模板题,以下程序分别用树形DP和两次BFS来求解. 树形DP: #include <cstdio> #i ...
- POJ 2631 Roads in the North (模板题)(树的直径)
<题目链接> 题目大意:求一颗带权树上任意两点的最远路径长度. 解题分析: 裸的树的直径,可由树形DP和DFS.BFS求解,下面介绍的是BFS解法. 在树上跑两遍BFS即可,第一遍BFS以 ...
- 与图论的邂逅01:树的直径&基环树&单调队列
树的直径 定义:树中最远的两个节点之间的距离被称为树的直径. 怎么求呢?有两种官方的算法(不要问官方指谁我也不晓得): 1.两次搜索.首先任选一个点,从它开始搜索,找到离它最远的节点x.然后从x开始 ...
- D4 树的直径、重心以及基环树
第一题第二题鉴上我前几篇博客poj1985 poj1849:https://www.cnblogs.com/Tyouchie/p/10384379.html 第三题:数的重心:poj1655 来自sj ...
- 【UOJ #351】新年的叶子(树的直径,期望)
题目链接 这的确是一道好题,我们不妨依循思路一步步推导,看问题是如何被解决的. 做一些约定,设$m$为树的叶子节点个数,设$len$为该树的直径(经过的点数). 毫无疑问,直径可能有多条,我们需要把所 ...
- 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分
树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...
随机推荐
- Linux进程和端口互相查看方法
一.查找应用进程的端口 ps -ef |grep mysql 查看进程 ps:将某个进程显示出来 -A 显示所有程序. -e 此参数的效果和指定"A"参数相同. -f 显示UID ...
- Eclipse 那些小技巧(值得收藏)
1.菜单命令系列 Edit→content Assist→add Alt+/ 代码关联 Windows→Next Editor→add Ctrl+Tab 切换窗口 Run→Debug Toggle L ...
- .Karma+Jasmine+karma-coverage
单元测试(模块测试)是开发者编写的一小段代码,用于检验被测代码的一个很小的.很明确的功能是否正确.通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为. Karma是一个基于N ...
- Linux-day2-pdf课件
1.Linux文件属性和权限 2.Linux重定向 3.Linux文件查找 4.Linux压缩打包 5.课堂作业 权限相关作业: 题目创建用户carol,ivy,jenny,kevin,alice创建 ...
- vue获取当前对象
<li v-for="img in willLoadImg" @click="selectImg($event)"> <img class=& ...
- redux+saga+reducer
saga.js这个文件里面的函数实际没有在其他jsx中引用吧?这个文件的作用就是把异步数据拿到,放进reducer,如果jsx想取,需要结合connect来取数据.
- springboot添加邮件发送及压缩功能
springboot添加邮件发送及文件压缩功能 转载请注明出处:https://www.cnblogs.com/funnyzpc/p/9190233.html 先来一段诗 ``` 就这样吧 忍受折磨 ...
- webpack学习--安装
webpack需要在node环境运行,可以去node官网进行下载安装包:http://nodejs.cn/download/ 1.打开cmd命令窗口,运行node -v 2.全局安装webpack:n ...
- vue的父子组建之间的通信(-),基于props和$emit之间的传递
对于vue而言,以为其核心思想为前端组建化.所以组建之间的通信必不可少. 相信接触过Angularjs的童鞋都知道angularjs的控制器之间的通信机制. 1:父传子:官方的$broadcast() ...
- vs2010 sp1 安装Silverlight5 语言版本不匹配的问题
好久之前用silverlight写了个程序,今天心血来潮想给朋友看一下,朋友更新了sl5,但是运行不起来. 所以有点郁闷,于是打算更新项目到silverlight5. 装sp1后,下载silverli ...