树的直径的求法即相关证明【树形DP || DFS】
学习大佬:树的直径求法及证明
树的直径
定义:
一棵树的直径就是这棵树上存在的最长路径。
给定一棵树,树中每条边都有一个权值,树中两点之间的距离定义为连接两点的路径边权之和。树中最远的两个节点之间的距离被称为树的直径,连接这两点的路径被称为树的最长链。后者通常也可称为直径,即直径是一个数值概念,也可代指一条路径。
求法:
一、树形dp
时间复杂度:O( n );
优点:代码量少实现方便。
不足:不容易记录路径。
实现过程:
状态:d[ x ] 以当前结点 x 为根的 子树的直径。
我们枚举每一个结点 x 以及 它要到达的下一个结点 Eiv。

这两个结点所能达到的最大距离之和 加上 这两个结点的边权就有可能去更新树的直径。
即:树的直径 ans_max = max{ d[ x ] + d[ Eiv ] + edge[x, Eiv] } (1 <= x <= N)
那么 d[ x ] 通过什么更新呢?当然是由 它所连接下一个结点所能达到最大距离 来更新了;
即 d[ x ] = max{ d[ x ], d[ Eiv ] + edge[ x, Eiv ] };
核心代码:
void dp(int st)
{
vis[st] = true; //当前结点已访问
for(int i = head[st]; i != -; i = edge[i].nxt){
int Eiv = edge[i].v;
if(vis[Eiv]) continue; //不走回头路
dp(Eiv);
ans_max = max(ans_max, d[st] + d[Eiv] + edge[i].w); //更新树的直径(由当前结点两段之和更新)
d[st] = max(d[st], d[Eiv]+edge[i].w); //更新当前结点所能走的最长路径(保留较长的那边)
}
}
二、DFS || BFS
时间复杂度: O( n );
优点:可以在第一次 dfs/bfs记录前驱
不足:代码量稍大
实现过程:
前提:
两次dfs或bfs。第一次任意选一个点进行dfs(bfs)找到离它最远的点,此点就是最长路的一个端点,再以此点进行dfs(bfs),找到离它最远的点,此点就是最长路的另一个端点,于是就找到了树的直径。
证明(by大佬):
假设此树的最长路径是从s到t,我们选择的点为u。反证法:假设搜到的点是v。
1、v在这条最长路径上,那么dis[u,v]>dis[u,v]+dis[v,s],显然矛盾。
2、v不在这条最长路径上,我们在最长路径上选择一个点为po,则dis[u,v]>dis[u,po]+dis[po,t],那么有dis[s,v]=dis[s,po]+dis[po,u]+dis[u,v]>dis[s,po]+dis[po,t]=dis[s,t],即dis[s,v]>dis[s,t],矛盾。
也许你想说u本身就在最长路径,或则其它的一些情况,但其实都能用类似于上面的反证法来证明的。
综上所述,你两次dfs(bfs)就可以求出最长路径的两个端点和路径长度。
核心代码:
void dfs(int s)
{
for(int i = head[s]; i != -; i = edge[i].nxt){
int Eiv = edge[i].v;
if(fa[s] == Eiv) continue; //不走回头路,也可以递归父亲结点省去fa数组空间
fa[Eiv] = s;
dis[Eiv] = dis[s] + edge[i].w; //当前结点最长路径
dfs(Eiv);
}
}
三、举栗子
Cow Marathon
| Time Limit: 2000MS | Memory Limit: 30000K | |
| Total Submissions: 6925 | Accepted: 3279 | |
| Case Time Limit: 1000MS | ||
Description
Input
Output
Sample Input
7 6
1 6 13 E
6 3 9 E
3 5 7 S
4 1 3 N
2 4 20 W
4 7 2 S
Sample Output
52
Hint
题意概括:
建一颗 N 个节点 M 条边的树,求树上两点最长距离(树的直径);方向都是虚的,建双向边。
解题思路:
①树形dp
AC code:
//树形dp版
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define INF 0x3f3f3f3f
using namespace std;
const int MAXN = 5e4+;
int d[MAXN];
bool vis[MAXN];
int head[MAXN], cnt;
int N, M, ans_max;
struct Edge
{
int v, w, nxt;
Edge(int _v = , int _w = , int _nxt = ):v(_v), w(_w), nxt(_nxt){};
}edge[MAXN<<]; void init()
{
memset(head, -, sizeof(head));
memset(d, , sizeof(d));
memset(vis, false, sizeof(vis));
cnt = ;
} void AddEdge(int from, int to, int weight)
{
edge[cnt] = Edge(to, weight, head[from]);
head[from] = cnt++;
} void dp(int st)
{
vis[st] = true; //当前结点已访问
for(int i = head[st]; i != -; i = edge[i].nxt){
int Eiv = edge[i].v;
if(vis[Eiv]) continue; //不走回头路
dp(Eiv);
ans_max = max(ans_max, d[st] + d[Eiv] + edge[i].w); //更新树的直径(由当前结点两段之和更新)
d[st] = max(d[st], d[Eiv]+edge[i].w); //更新当前结点所能走的最长路径(保留较长的那边)
}
} int main()
{
while(~scanf("%d%d", &N, &M))
{
init();
char ccc;
for(int i = , u, v, w; i <= M; i++){
scanf("%d%d%d %c", &u, &v, &w, &ccc);
AddEdge(u, v, w);
AddEdge(v, u, w);
}
//printf("%d\n", ans);
ans_max = ;
dp();
printf("%d\n", ans_max);
}
return ;
}
②两次DFS
AC code:
//DFS版
/*
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define INF 0x3f3f3f3f
using namespace std;
const int MAXN = 5e4+5;
int dis[MAXN], fa[MAXN];
int head[MAXN], cnt;
int N, M, ans; struct Edge
{
int v, w, nxt;
Edge(int _v = 0, int _w = 0, int _nxt = 0):v(_v), w(_w), nxt(_nxt){};
}edge[MAXN<<1]; void AddEdge(int from, int to, int weight)
{
edge[cnt] = Edge(to, weight, head[from]);
head[from] = cnt++;
} void init()
{
memset(head, -1, sizeof(head));
for(int i = 0; i <= N; i++) fa[i] = i;
cnt = 0;
ans = 0;
} void dfs(int s)
{
for(int i = head[s]; i != -1; i = edge[i].nxt){
int Eiv = edge[i].v;
if(fa[s] == Eiv) continue; //不走回头路,也可以递归父亲结点省去fa数组空间
fa[Eiv] = s;
dis[Eiv] = dis[s] + edge[i].w; //当前结点最长路径
dfs(Eiv);
}
} int main()
{
while(~scanf("%d%d", &N, &M))
{
init();
char ccc;
for(int i = 1, u, v, w; i <= M; i++){
scanf("%d%d%d %c", &u, &v, &w, &ccc);
AddEdge(u, v, w);
AddEdge(v, u, w);
}
//printf("%d\n", ans);
int ans_max = 0, ans_index = 0;
dfs(1); //第一次dfs找出树的直径所在的点
for(int i = 1; i <= N; i++){
if(dis[i] > ans_max){
ans_max = dis[i];
ans_index = i;
}
dis[i] = 0;
fa[i] = i;
} dfs(ans_index); //第二次dfs找出树的直径
for(int i = 1; i <= N; i++){
if(dis[i] > ans_max) ans_max = dis[i];
}
printf("%d\n", ans_max);
}
return 0;
}
Caterpillar
| Time Limit: 2000MS | Memory Limit: 65536K | |
| Total Submissions: 2505 | Accepted: 1180 |
Description
An undirected graph is called a caterpillar if it is connected, has no cycles, and there is a path in the graph where every node is either on this path or a neighbor of a node on the path. This path is called the spine of the caterpillar and the spine may not be unique. You are simply going to check graphs to see if they are caterpillars.
For example, the left graph below is not a caterpillar, but the right graph is. One possible spine is
shown by dots.

Input
There will be multiple test cases. Each test case starts with a line containing n indicating the number of nodes, numbered 1 through n (a value of n = 0 indicates end-of-input). The next line will contain an integer e indicating the number of edges. Starting on the following line will be e pairs n1 n2 indicating an undirected edge between nodes n1 and n1. This information may span multiple lines. You may assume that n ≤ 100 and e ≤ 300. Do not assume that the graphs in the test cases are connected or acyclic.
Output
For each test case generate one line of output. This line should either be
Graph g is a caterpillar.
or
Graph g is not a caterpillar.
as appropriate, where g is the number of the graph, starting at 1.
Sample Input
22
21
1 2 2 3 2 4 2 5 2 6 6 7 6 10 10 8 9 10 10 12 11 12 12 13 12 17
18 17 15 17 15 14 16 15 17 20 20 21 20 22 20 19
16
15
1 2 2 3 5 2 4 2 2 6 6 7 6 8 6 9 9 10 10 12 10 11 10 14 10 13 13 16 13 15
0
Sample Output
Graph 1 is not a caterpillar.
Graph 2 is a caterpillar.
题意概括:
首先需要判断给的图是不是一棵树;
其次需要判断是否存在一条路径使得图上所有的点要么在这条路径上,要么距离该路径的距离为 1。
解题思路:
判断是否为一棵树直接dfs判断是否存在环即可;
为了让最多的点满足第二个条件,则这条路径一定是树的直径。
两次DFS求出树的直径,遍历一遍判断是否所有点都满足条件。
为了做这个判断,DFS时不仅要更新树的直径还要同时更新每一个结点能达到的最长路径;
如果当前结点能达到的最长路径等于树的直径说明该结点在树的直径上,如果不能则判断他是否能通过相连的结点到达树的直径。
AC code:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int MAXN = ;
struct Edge{int v; int nxt;}edge[MAXN<<];
int head[MAXN], cnt;
int dis[MAXN], bb[MAXN];
bool vis[MAXN];
int N, M;
int ans_max, ans_index;
bool chck; void init()
{
memset(head, -, sizeof(head));
memset(vis, false, sizeof(vis));
memset(dis, , sizeof(dis));
memset(bb, , sizeof(bb));
cnt = ;
chck = false;
ans_max = ; ans_index = ;
} void AddEdge(int from, int to)
{
edge[cnt].v = to;
edge[cnt]. nxt = head[from];
head[from] = cnt++;
} void check(int s, int fa)
{
vis[s] = true;
for(int i = head[s]; i != -; i = edge[i].nxt){
int Eiv = edge[i].v;
if(Eiv == fa) continue;
if(vis[Eiv]) {chck = true; return;}
check(Eiv, s);
if(chck) return;
}
} void dfs(int s, int fa)
{
for(int i = head[s]; i != -; i = edge[i].nxt){
int Eiv = edge[i].v;
if(Eiv != fa){
dis[Eiv] = dis[s] + ;
bb[Eiv] = dis[Eiv];
if(dis[Eiv] > ans_max) {ans_max = dis[Eiv]; ans_index = Eiv;} //更新树的直径
dfs(Eiv, s);
bb[s] = max(bb[s], bb[Eiv]); //更新当前结点最长路径
}
}
} int main()
{
int T_case = ;
while(~scanf("%d", &N) && N){
scanf("%d", &M);
init();
for(int m = , u, v; m <= M; m++){
scanf("%d%d", &u, &v);
AddEdge(u, v);
AddEdge(v, u);
} if(M > N-) chck = true;
if(!chck) check(, ); //判断是否存在环
if(!chck){
for(int i = ; i <= N; i++){
if(!vis[i]){chck = true; break;} //判断所有点是否联通
}
if(!chck){
dis[] = ;
dfs(, ); //第一次dfs找出树的直径所在的点
dis[ans_index] = ;
dfs(ans_index, ); //第二次dfs找出树的直径
for(int i = ; i <= N; i++){ //判断所有的点是否满足条件
bool flag = false;
if(bb[i] == ans_max) continue; //在直径上
for(int k = head[i]; k != -; k = edge[k].nxt){ //判断不在树的直径上的点到树的直径距离是否为 1
if(bb[edge[k].v] == ans_max) {flag = true; break;}
}
if(!flag) {chck = true; break;}
}
}
}
if(chck) printf("Graph %d is not a caterpillar.\n", ++T_case);
else printf("Graph %d is a caterpillar.\n", ++T_case);
}
return ;
}
树的直径的求法即相关证明【树形DP || DFS】的更多相关文章
- 树的直径新求法、codeforces 690C3 Brain Network (hard)
树的直径新求法 讲解题目 今天考了一道题目,下面的思路二是我在考场上原创,好像没人想到这种做法,最原始的题目,考场上的题目是这样的: 你现在有1 个节点,他的标号为1,每次加入一个节点,第i 次加入的 ...
- HDU 5293 Annoying problem 树形dp dfs序 树状数组 lca
Annoying problem 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5293 Description Coco has a tree, w ...
- 树形DP+DFS序+树状数组 HDOJ 5293 Tree chain problem(树链问题)
题目链接 题意: 有n个点的一棵树.其中树上有m条已知的链,每条链有一个权值.从中选出任意个不相交的链使得链的权值和最大. 思路: 树形DP.设dp[i]表示i的子树下的最优权值和,sum[i]表示不 ...
- HDU 5293 Tree chain problem 树形dp+dfs序+树状数组+LCA
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5293 题意: 给你一些链,每条链都有自己的价值,求不相交不重合的链能够组成的最大价值. 题解: 树形 ...
- 刷题总结——Tree chain problem(HDU 5293 树形dp+dfs序+树状数组)
题目: Problem Description Coco has a tree, whose vertices are conveniently labeled by 1,2,…,n.There ar ...
- SPOJ 1479 +SPOJ 666 无向树最小点覆盖 ,第二题要方案数,树形dp
题意:求一颗无向树的最小点覆盖. 本来一看是最小点覆盖,直接一下敲了二分图求最小割,TLE. 树形DP,叫的这么玄乎,本来是线性DP是线上往前\后推,而树形DP就是在树上,由叶子结点状态向根状态推. ...
- BZOJ4784 ZJOI2017仙人掌(树形dp+dfs树)
首先考虑是棵树的话怎么做.可以发现相当于在树上选择一些长度>=2的路径使其没有交,同时也就相当于用一些没有交的路径覆盖整棵树. 那么设f[i]为覆盖i子树的方案数.转移时考虑包含根的路径.注意到 ...
- Codeforces 592D - Super M - [树的直径][DFS]
Time limit 2000 ms Memory limit 262144 kB Source Codeforces Round #328 (Div. 2) Ari the monster is n ...
- poj:1985:Cow Marathon(求树的直径)
Cow Marathon Time Limit: 2000MS Memory Limit: 30000K Total Submissions: 5496 Accepted: 2685 Case ...
随机推荐
- Lucene常用类
1.1 IndexWriter: 充当 创造/在索引过程中更新指标的 核心组成部分 1.2 Lucene目录 Directory: 索引的存储位置: 通常是文件的列表: 这些文件被称为索引文件. ...
- python 爬虫系列03--职位爬虫
职位爬虫 import requests from lxml import etree cookie = { 'Cookie':'user_trace_token=20181015184304-692 ...
- daterangepicker
官方文档 http://www.daterangepicker.com/#examples 与angular结合 html <div date-range-picker class=" ...
- 白话SpringCloud | 第六章:Hystrix监控面板及数据聚合(Turbine)
前言 前面一章,我们讲解了如何整合Hystrix.而在实际情况下,使用了Hystrix的同时,还会对其进行实时的数据监控,反馈各类指标数据.今天我们就将讲解下Hystrix Dashboard和Tur ...
- tomcat-dbcp数据库连接池配置以及使用时候的一些坑
一.数据库连接池 开发的时候经常会需要对数据库进行一些操作,比如说常见的增删改查之类的,当数据量小的时候,可以直接进行操作,但是当数据量增多的时候,每一次连接以及释放数据库都会耗费一定的时间,这个时候 ...
- Python实现抓取CSDN博客首页文章列表
1.使用工具: Python3.5 BeautifulSoup 2.抓取网站: csdn首页文章列表 http://blog.csdn.net/ 3.分析网站文章列表代码: 4.实现抓取代码: __a ...
- IDEA+Maven+多个SpringBoot子模块(创建多模块整合项目)
https://blog.csdn.net/willjgl/article/details/77773634 https://blog.csdn.net/qqHJQS/article/details/ ...
- stark——分页、search、actions
一.分页 1.引入自定义分页组件 在/stark目录下创建utils工具包目录,复制page.py到该目录下,文件中有之前自定义的分页组件. class Pagination(object): def ...
- Disruptor之粗糙认识
一 概述 1.Disruptor Disruptor是一个高性能的异步处理框架,一个“生产者-消费者”模型. 2.RingBuffer RingBuffer是一种环形数据结构,包含一个指向下一个槽点的 ...
- 网易回合制游戏录像批量下载(失效 不是因为代码 是因为网易官方关闭了录像网站 :P)
最近在访问网易大话西游2的录像专区时,发现页面还是很早之前的板式,网易的编辑并没有打算重新美化的打算,不由得内心一寒,结合之前好几个回合制游戏的倒闭,让很多人回顾都没办法回顾, 而且,很多人现在也没有 ...