0714-浅谈树形dp-典型例题三

我也不知道csdn出什么鬼毛病了,但我的典例二就是保存不起,╮(╯▽╰)╭我们先将就讲讲三吧

类型三:求树的最长链问题

我们先看看题,明确最长链的定义,就是树的直径

树的直径


描述

树的直径,即这棵树中距离最远的两个结点的距离。每两个相邻的结点的距离为1,即父亲结点与儿子结点或儿子结点与父子结点之间的距离为1.有趣的是,从树的任意一个结点a出发,走到距离最远的结点b,再从结点b出发,能够走的最远距离,就是树的直径。树中相邻两个结点的距离为1。你的任务是:给定一棵树,求这棵树中距离最远的两个结点的距离。

输入

输入共n行 第一行是一个正整数n,表示这棵树的结点数 接下来的n-1行,每行三个正整数a,b,w。表示结点a和结点b之间有一条边,长度为w 数据保证一定是一棵树,不必判错。

输出

输出共一行 第一行仅一个数,表示这棵树的最远距离

样例输入[复制]
4
1 2 10
1 3 12
1 4 15
样例输出[复制]
27
提示及题解

数据范围和注释 Hint

10%的数据满足1<=n<=5

40%的数据满足1<=n<=100

100%的数据满足1<=n<=10000 1<=a,b<=n 1<=w<=10000



我觉得在框框里写挺好的,大家将就一下

方法一:树形dp

方法二:两次DFS

我们就先契合主题,讲一下树形dp。

还是和我们之前一样,分析每一个点,发现最长链与它的关系那么我们状态就好定义了,而且我们也知道,某子树的直径是由它的最长链和次长链组成的那么我们就用d1[i]表示以i为根的子树中最长链的长度,d2[i]表示以i为根的子树中次长链的长度。而对于1号节点而言,最长链要么经过它,要么不经过它。但反正求的ans总是取的max,所以不用单独弄出来搞

现在考虑如何状态转移:

用 j 来表示 i 的某儿子,那么如果d1[j]+len>d1[i] 那么就更新d1[i],d2[i]的值,为什么还要更新d2[i]呢?因为最长链更新后,次长链就等于原来的最长链了,如果d1[j]+len>d2[i] 就更新次长链

P.S.不用单独建图,(我想了半天,但老师说用不着),直接在dfs时把它当成图来解决就是了。然后我傻逼的用数组来搞,20009*10009,然后就MLE了,悲伤%>_<%,最后发现用前向星或vector来搞就ok啦

这里我用了vector,差点gg


#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<vector>
using namespace std;
int n,a,b,w;

int ans=-1;
struct node{
	int v,len;
};
vector<node> f[10009];
int l1[10009],l2[10009];
void dfs(int u,int fa){
	int i,j,k;
	for(i=0;i<f[u].size();++i){//从0开始!!!
		int v=f[u][i].v,len=f[u][i].len;
		if(v==fa) continue;
		dfs(v,u);
		if(l1[v]+len>l1[u]) l2[u]=l1[u],l1[u]=l1[v]+len;
		else if(l1[v]+len>l2[u]) l2[u]=l1[v]+len;
	}
	ans=max(ans,l1[u]+l2[u]);
}
int main(){
	scanf("%d",&n);
	int i,j,k;
	for(i=1;i<n;++i)
	{
		scanf("%d%d%d",&a,&b,&w);
		f[a].push_back((node){b,w});
		f[b].push_back((node){a,w});
	}
	dfs(1,0);
	printf("%d",ans);
	return 0;
}

好啦,树形dp over了,该来看看两次dfs怎么搞了
从树的任意一个结点p出发,走到距离最远的结点q,再从结点q出发,能够走的最远距离,就是树的直径。”是从这一句话入手,想到的dfs,但是,为什么正确呢?我们来证明一下吧
①若P在直径上,则Q一定在直径上; 
②若P不在直径上,设直径为AB  若AB与PQ有交点C,则CQ>CA或CB(P到Q最远),那么 AC+CQ>AB或BC+CQ>AB,就与AB是直径矛盾。所以Q一定=A或B; 若AB与PQ无交点,PQ上某一点M到AB上某一点N,则 MQ+MN<NB(与N最远的点A和B),则NM+NB>MQ,即 PM+MN+NB>PM+MQ=PQ,但是这显然不可能,因为已知P的最 远点是Q,所以AB与PQ不可能无交点。 

证毕

那么我们dfs的目的找到了,dfs(i)就是要找离i距离最远的点,该怎么实现呢?对于每个节点i,f[i]=max(dfs(son[i])+len),与此同时记录下,到达的最远的那个点的编号,再进行一次dfs即可



猜你喜欢

转载自blog.csdn.net/weixin_42557561/article/details/81041275