hdu Computer 2196 树形dp
http://acm.hdu.edu.cn/showproblem.php?pid=2196
看了很长时间都没看懂,解题报告也是乱的让人头疼。最后问了一下von的思路,然后看了看代码终于明白了。这题真的很难。
首先按所给信息建图,建立的是有向图。然后将1视为根节点第一遍dfs从叶往根递推,求出每个点所能到达的最远距离和次远距离,只有当这一个点有多条分支时才存在次远距离(注意这里是有方向);
该点所能到达的最远距离可能是第一遍dfs搜到最远距离,也有可能由父亲节点+父亲节点与该节点的距离所得。所以第二遍dfs从根节点1出发往树叶边遍历。
f[i]存最长距离,l[i]存次长距离,dp[i]存由父亲节点延伸后可能到达的距离,dir[i]存i节点取f[i]时的路径方向,防止与求次长距离时路径重复,以及后面求dp[i]时的判断
View Code
#include <cstdio>
#include <cstring>
#include <vector>
#define maxn 10005
using namespace std;
struct node
{
int v;
int val;
};
vector<node>g[maxn];
int f[maxn],dp[maxn],l[maxn],dir[maxn];
void dfs_1(int rpos)
{
int i;
int max = 0,num;
int flag1 = -1;
int flag2 = -1;
if(f[rpos]) return ;
int size = g[rpos].size();
if(size == 0) return ;
for (i = 0; i < size; ++i)
{
num = g[rpos][i].v;
dfs_1(num);
if (f[num] + g[rpos][i].val > max)//求rpos的最长距离
{
max = f[num] + g[rpos][i].val;
flag1 = i;
}
}
f[rpos] = max;
dir[rpos] = flag1;//记录方向
max = 0;
for (i = 0; i < size; ++i)
{
num = g[rpos][i].v;
if(i != flag1 && f[num] + g[rpos][i].val > max)//求次长距离
{
max = f[num] + g[rpos][i].val;
flag2 = i;
}
}
if (flag2 != -1) l[rpos] = max;
}
void dfs_2(int rpos)
{
int i;
int size = g[rpos].size();
for (i = 0; i < size; ++i)
{
int num = g[rpos][i].v;
if (i == dir[rpos])
dp [num] = max(l[rpos],dp[rpos]) + g[rpos][i].val;
//当这点在f[rpos]的路径上时可能是父亲节点的次长距离+val也可能是dp[rpos]+val
else
dp [num] = max(f[rpos],dp[rpos]) + g[rpos][i].val;
//当这点不在f[rpos]的方向上时,可能是父亲节点的最长距离+val也可是do[rpos]+val;
dfs_2(num);
}
}
int main()
{
int n,i,j,x,y;
node temp;
while (~scanf("%d",&n))
{
for (i = 0; i <= n; ++i)
g[i].clear();
for (i = 2; i <= n; ++i)
{
scanf("%d %d",&x,&temp.val);
temp.v = i;
g[x].push_back(temp);//建立有向图
}
for (i = 0; i <= maxn; ++i)
{
f[i] = dp[i] = l[i] = 0;
}
dfs_1(1);
/*for (i = 1; i <= n; ++i)
printf("%d %d\n",f[i],l[i]);
printf("\n");*/
dfs_2(1);
/*for (i = 1; i <= n; ++i)
printf("%d %d %d\n",f[i],l[i],dp[i]);
printf("\n");*/
for (i = 1; i <= n; ++i)
{
printf("%d\n",max(f[i],dp[i]));//最后在两者之间做出选择
}
}
return 0;
}