『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 ...
随机推荐
- JVM调优入门之初探
JVM:程序计数器,jvm栈,本地方法栈,堆,方法区 JVM:虚拟机内存又分有:年轻代(eden,servivor s0,servivor s1),年老代(tenured),永久代() 问题1:如何查 ...
- 网络攻击技术:SQL Injection(sql注入)
网络攻击技术开篇——SQL Injection 1.1.1 摘要 日前,国内最大的程序员社区CSDN网站的用户数据库被黑客公开发布,600万用户的登录名及密码被公开泄露,随后又有多家网站的用户密码 ...
- 微软官网tools
DHCP/AD域插件: 远程管理工具(含DHCP/AD域) 安装网址: https://www.microsoft.com/zh-cn/download/details.aspx?id=7887 程序 ...
- nodejs中的Buffer
一,开篇分析 所谓缓冲区Buffer,就是 "临时存贮区" 的意思,是暂时存放输入输出数据的一段内存. JS语言自身只有字符串数据类型,没有二进制数据类型,因此NodeJS提供了一 ...
- 简单使用WebSocket实现聊天室
环境需求:flask,websocket第三方包 目录结构 web中实现群聊 ws_群聊.py文件 # 实现一个websocket 先下载包 gevent-websocket from flask i ...
- Capslock+ 键盘党都爱的高效利器 - 让 Windows 快捷键操作更加灵活强大
Capslock+ 键盘党都爱的高效利器 - 让 Windows 快捷键操作更加灵活强大 优化辅助 Windows 2016-06-05 91,167 微博微信QQ空间印象有道邮件 ...
- The First of my text
JavaScript 学习第一天 一.属性可以动态拓展 例如: var person = { name : "zs"; age : 21; } person.sex = " ...
- System.InvalidOperationException: 支持“XXX”上下文的模型已在数据库创建后发生更改。请考虑使用 Code First 迁移更新数据库(http://go.microsoft.com/fwlink/?LinkId=238269)。
System.InvalidOperationException: 支持“XXX”上下文的模型已在数据库创建后发生更改.请考虑使用 Code First 迁移更新数据库(http://go.micro ...
- centos7系统下搭建docker本地镜像仓库
## 准备工作 用到的工具, Xshell5, Xftp5, docker.io/registry:latest镜像 关于docker的安装和设置加速, 请参考这篇博文centos7系统下 docke ...
- AES加密算法详解
AES 是一个对称密码分组算法,分组长度为128bit,密钥长度为128.192 和 256 bit. 整个加密过程如下图所示. 1.密钥生成算法 密钥扩展过程: 1) 将种子密钥按下图所示的格式排 ...