我也不知道csdn出什么鬼毛病了,但我的典例二就是保存不起,╮(╯▽╰)╭我们先将就讲讲三吧
类型三:求树的最长链问题
我们先看看题,明确最长链的定义,就是树的直径
树的直径
描述
树的直径,即这棵树中距离最远的两个结点的距离。每两个相邻的结点的距离为1,即父亲结点与儿子结点或儿子结点与父子结点之间的距离为1.有趣的是,从树的任意一个结点a出发,走到距离最远的结点b,再从结点b出发,能够走的最远距离,就是树的直径。树中相邻两个结点的距离为1。你的任务是:给定一棵树,求这棵树中距离最远的两个结点的距离。
输入
输入共n行 第一行是一个正整数n,表示这棵树的结点数 接下来的n-1行,每行三个正整数a,b,w。表示结点a和结点b之间有一条边,长度为w 数据保证一定是一棵树,不必判错。
输出
输出共一行 第一行仅一个数,表示这棵树的最远距离
样例输入[复制]
1 2 10
1 3 12
1 4 15
样例输出[复制]
提示及题解
数据范围和注释 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;
}
证毕
那么我们dfs的目的找到了,dfs(i)就是要找离i距离最远的点,该怎么实现呢?对于每个节点i,f[i]=max(dfs(son[i])+len),与此同时记录下,到达的最远的那个点的编号,再进行一次dfs即可