省赛快到了,打算练熟一下树形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 ",ans); return 0; }