首先不得不说这题真的很不错 花了我一整天的时间来想= =
题目要求大概就是给你一棵树,求每一个节点到达的其他点所经过边的权值的最大值
开始还想用FLOYD 但是看到10000个结点= =顿时打消了念头
后来想到应该树形dp
对于一个结点,他能取得的最大值有2种可能,1是从他的子树走,2是从父节点走
所以
用far数组表示一个节点从他的子树走所能到达的最大权值
second数组表示次远值(这个数组表示的边不能与far数组表示的边有交集)
max_v数组表示取得far数组值时该节点走向的子树的根节点
from_fathershu数组表示从父亲节点走所能取得的最大权值
比如一棵树
1
(2) / \ (1)
2 4
(1) / \(1)
3 5
括号表示那条边的权值
这棵树中far[1] = 3(1-2-3这条路径最大),second[1] = 2(不能走1-2-3这条路径,只能从另外一边走,如果没有的话另一条边那就为0)
max_v[1] = 2(从以2为根这棵子树走的时候取得最大值),from_father[1] = 0(没有父亲结点,所以为0)from_father[4] = 4(4-1-2-3取得最大值)
然后dfs2次,第一次先求出far和second状态转移方程很好写
far[i] = max(far[i],son[i]+val[i,son[i]]);(val[i,son[i]]表示i和son[i]相连这条边的权值)
second[i]就在求far的过程中动态选择 直接看代码吧
max_v在求far的时候也很容易就拿到了
第二次dfs,我们求from_father数组
状态转移方程
if(j是i的儿子){
if(j==max_v[i]) from_father[j] = max(from_father[i],second[i])+val[i,j];(这时候,j在i取得最大权值的那条路上,所以不能从取得最大权值的那条路走,只能从次大权值路走,还可能继续由父节点的父节点走)
else from_father[j] = max(from_father[i],far[i])+val[i,j];(这时候,可能由父节点的父节点走,也可能由父节点的最大权值路走)
}
下面是代码
#include <iostream>
#include <vector>
#include <string.h>
using namespace std;
int n;
vector<int> son[10001];//结点的儿子
int val[10001];
int far[10001],second[10001],max_v[10001],from_father[10001];
void dfs(int u)
{
max_v[u] = u;
for(int i = 0; i<son[u].size(); i++)
{
int v = son[u][i];
dfs(v);
if(far[u]<far[v]+val[v])
{
second[u] = far[u];
far[u] = far[v]+val[v];
max_v[u] = v;
}
else if(far[v]+val[v]>second[u])
second[u] = far[v]+val[v];
}
}
void dfs2(int u)
{
for(int i = 0; i<son[u].size(); i++)
{
int v = son[u][i];
if(v==max_v[u]) from_father[v] = max(from_father[u],second[u])+val[v];
else from_father[v] = max(from_father[u],far[u])+val[v];
dfs2(v);
}
}
int main(void)
{
cin.sync_with_stdio(false);
cout.sync_with_stdio(false);
while(cin>>n)
{
for(int i = 0; i<=n; i++)
{
son[i].clear();
}
int a,b;
for(int i = 2; i<=n; i++)
{
cin>>a>>b;
son[a].push_back(i);
val[i] = b;
}
val[1] = 0;
memset(far,0,sizeof(int)*(n+1));
memset(second,0,sizeof(int)*(n+1));
memset(from_father,0,sizeof(int)*(n+1));
dfs(1);
dfs2(1);
for(int i = 1; i<=n; i++) cout<<max(far[i],from_father[i])<<endl;
}
}