省赛快到了,打算练熟一下树形DP。。看的学长介绍的论文上的题(好难啊orz
整理了很久的思路,结合别人的博客才有一点看见答案的感觉
以点1为根
第一遍DFS,记录每个点其儿子到它的距离最大值和次大值,并且记录来源。
第二遍DFS,考虑当前的点X为汇聚点, -( 这样图中间那个点
要求到x点的三个最远点,分别为x的子树到x的最大值和次大值,剩下一个点在x的父节点为根的树取
(如果来源不是x取最大,否则取次大)
三个最值排序后记为a,b,c,ans=max(ans,a+2*b+c)
原文:http://3y.uu456.com/bp_5x3ew02tj60a6ri16zrz_1.html
下面还有另外一条树形DP,记得要看。。
------------------------------------------------------------------------------------------
参考另一篇博客:http://blog.csdn.net/qpswwww/article/details/46859293
得知结论min(d(x,z),d(y,z))+d(x,y)最大时,x-y必定是树的最大直径
这样就可以通过枚举z的位置来求
不错的论文补充:http://blog.csdn.net/gauss_acm/article/details/40625147
参考代码:(原文http://blog.csdn.net/cynthia_wjyi/article/details/50381405
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=200005;
struct Node{
int to,next,w;
}e[maxn<<1];
int tot,head[maxn],n,m;
ll mx1[maxn],mx2[maxn],mx3[maxn],f[maxn],ans;
bool vis[maxn];
void add(int u,int v,int w){
e[++tot]=(Node){v,head[u],w};head[u]=tot;//边从1开始命名,head[u]:以u开头的边的最后一条的编号
}
void dfs(int x){ //每个点保存子链过来的三个最长的距离(存至多三条子链,不满算0
vis[x]=1;mx1[x]=mx2[x]=0;
for(int i=head[x];i;i=e[i].next)if(!vis[e[i].to]){
dfs(e[i].to);
mx3[x]=max(mx3[x],mx1[e[i].to]+e[i].w);
if(mx3[x]>mx2[x])swap(mx3[x],mx2[x]);
if(mx2[x]>mx1[x])swap(mx1[x],mx2[x]);
}
}
void dfs1(int x){
vis[x]=1;
for(int i=head[x];i;i=e[i].next) //枚举x节点的子链,更新孩子父链的长度
if(!vis[e[i].to]){
f[e[i].to]=f[x]+e[i].w; //继承从根节点来的链长(因为之前是算的三条子链
if(mx1[e[i].to]+e[i].w==mx1[x]) //父节点最长链和子节点同向,就要用父节点的第二长链
f[e[i].to]=max(f[e[i].to],mx2[x]+e[i].w);
else
f[e[i].to]=max(f[e[i].to],mx1[x]+e[i].w);
dfs1(e[i].to);
}
}
void update(ll &x,ll &y,ll &z){
if(y>x)swap(x,y);
if(z>x)swap(x,z);
if(z>y)swap(y,z);
ans=max(ans,x+(y<<1)+z);
}
int main(){
scanf("%d%d",&n,&m);
int u,v,w;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
memset(vis,0,sizeof(vis));
dfs(1);
memset(vis,0,sizeof(vis));
dfs1(1);
ans=0;
for(int i=1;i<=n;i++)
f[i]<=mx3[i]?update(mx1[i],mx2[i],mx3[i]):update(mx1[i],mx2[i],f[i]);
printf("%lld\n",ans);
return 0;
}