hdu2196 树形dp

想法:对于一棵树,对于任意一个节点,它的最大值可以来自以它为根的子树,也可以来自它的父亲。
所以需要考虑二个方向的情况。所以必须考虑2个方向。
解法:dp[i][0]保存子树的值,dp[i][1]保存父亲结点方向+dis[i,root];
可以先一遍dfs,保存任意点子树的最大值,同时保存任意点取最大值的方向,同时维护他的第二大值。
然后进行第二次dfs,对于结点u,如果它的孩子不是它取最大值路径上的点,那么孩子的值dp[i][1]为
max(dp[root][1], dp[root][0])+ dis[i,root];不然dp[i][1]为父亲结点的第二大值 + dis[i,root];
然后第二大值为dp[i][1];

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MAXN = 10010;
struct node
{
    int to;
    int v;
    int next;
}edge[MAXN*3];
int pre[MAXN],index,vis[MAXN],n;
int dp[MAXN][2],way[MAXN],sv[MAXN];
//sv[]存第另一边的 不是子树的 way[]表示当前点最大值从哪里来
//dp[][0]表示子树方向的最大值 dp[][1]表示另一方向的最大值
int max(int x,int y){
    return x>y?x:y;
}
int min(int x,int y){
    return x<y?x:y;
}
void add(int x,int y,int z)
{
    edge[index].to = y;
    edge[index].v = z;
    edge[index].next = pre[x];
    pre[x] = index++;
}
void dfs1(int root)
{
    vis[root] = 1;
    int i;
    int set = root;
    for(i=pre[root]; i!=-1; i=edge[i].next){
        int t = edge[i].to;
        if(!vis[t]){
            dfs1(t);
            if(dp[root][0] > dp[t][0] + edge[i].v){
                sv[root] = max(sv[root], dp[t][0] + edge[i].v);
            }
            else {
                set = t;
                sv[root] = dp[root][0];
                dp[root][0] = dp[t][0] + edge[i].v;
            }
        }
    }
    way[root] = set;
}
void dfs2(int root)
{
    vis[root] = 1;
    int i;
    for(i=pre[root]; i!=-1; i=edge[i].next){
        int t = edge[i].to;
        if(!vis[t]){
            if(way[root] != t){
                dp[t][1] = max(dp[root][0], dp[root][1]) + edge[i].v;
                sv[t] = dp[t][1];
            }
            else {
                dp[t][1] = sv[root] + edge[i].v;
                sv[t] = dp[t][1];
            }
            dfs2(t);
        }
    }
}
int main()
{
    int i;
    while(~scanf("%d",&n))
    {
        index = 1;
        memset(sv,0,sizeof(sv));
        memset(dp,0,sizeof(dp));
        memset(pre,-1,sizeof(pre));
        for(i=2; i<=n; ++i){
            int x,v;
            scanf("%d %d",&x, &v);
            add(i, x, v);
            add(x, i, v);
        }
        memset(vis,0,sizeof(vis));
        dfs1(1);
        /*for(i=1; i<=n; i++){
            printf("%d  ",sv[i]);
        }
        cout<<endl;*/
        memset(vis,0,sizeof(vis));
        dfs2(1);
        for(i=1; i<=n; i++){
            printf("%d\n",max(dp[i][0],dp[i][1]));
        }
    }
}
/*
7
1 1
1 1
2 1
2 1
3 1
3 1
8
1 1
1 1
2 1
2 1
3 1
5 1
7 1
*/
时间: 2024-10-16 22:51:24

hdu2196 树形dp的相关文章

hdu2196树形dp

有一棵树,找每个节点所能到达的最远距离是多少 dis[u][0]正向最大距离    dis[u][1]正向次大距离     dis[u][2]反向最大距离 先一边dfs求出每个节点的正向最大距离(就是向下)和次向最大距离,以及longest记录下当前节点的正向最大距离路径的子节点(为了第二遍dfs,判断这一条路是不是正向最大距离) 再进行第二遍dfs,v是u 的子节点,如果v在u的正向最大距离路径里(即longest[u]=v)那么v的最大反向距离就是max(u的最大反向距离,u的正向次大距离)

【树形dp小练】HDU1520 HDU2196 HDU1561 HDU3534

[树形dp]就是在树上做的一些dp之类的递推,因为一般需要递归处理,因此平凡情况的处理可能需要理清思路.昨晚开始切了4题,作为入门训练.题目都非常简单,但是似乎做起来都还口以- hdu1520 Anniversary party 给一颗关系树,各点有权值,选一些点出来.任一对直接相连的边连接的点不能同时选择,问选择出的权值和最大为多少. 考虑以u为根的子树可以选择的方法,dp[u]表示选择u时能获得最大收益,dp2[u]表示不选u时能获得的最大收益.则u不选时,为dp2[u]=max{dp[v]

HDU-2196 Computer (树形DP)

最近在看树形DP,这题应该是树形DP的经典题了,写完以后还是有点感觉的.之后看了discuss可以用树分治来做,以后再试一试. 题目大意 找到带权树上离每个点的最远点.︿( ̄︶ ̄)︿ 题解: 对于每一个点的最远点,就是以这个点为根到所有叶子节点的最长距离.但是如果确定根的话,除了根节点外,只能找到每个节点(度数-1)个子树的最大值,剩下一个子树是该节点当前的父亲节点. 所以当前节点的最远点在当前节点子树的所有叶子节点以及父亲节点的最远点上(当父亲节点的最远点不在当前节点的子树上时), 如果父亲节

HDU2196 Computer(树形DP)

和LightOJ1257一样,之前我用了树分治写了.其实原来这题是道经典的树形DP,感觉这个DP不简单.. dp[0][u]表示以u为根的子树中的结点与u的最远距离 dp[1][u]表示以u为根的子树中的结点与u的次远距离 这两个可以一遍dfs通过儿子结点转移得到.显然dp[0][u]就是u的一个可能的答案,即u往下走的最远距离,还缺一部分就是u往上走的最远距离: dp[2][u]表示u往上走的最远距离 对于这个的转移,分两种情况,是这样的: dp[2][v] = max( dp[0][u]+w

树形 DP 总结

本文转自:http://blog.csdn.net/angon823/article/details/52334548 介绍 1.什么是树型动态规划 顾名思义,树型动态规划就是在"树"的数据结构上的动态规划,平时作的动态规划都是线性的或者是建立在图上的,线性的动态规划有二种方向既向前和向后,相应的线性的动态规划有二种方法既顺推与逆推,而树型动态规划是建立在树上的,所以也相应的有二个方向: 1.叶->根:在回溯的时候从叶子节点往上更新信息 2.根 - >叶:往往是在从叶往根d

Codeforces 543D Road Improvement(树形DP+乘法逆元)

题目大概说给一棵树,树的边一开始都是损坏的,要修复一些边,修复完后要满足各个点到根的路径上最多只有一条坏的边,现在以各个点为根分别求出修复边的方案数,其结果模1000000007. 不难联想到这题和HDU2196是一种类型的树形DP,因为它们都要分别求各个点的答案.然后解法也不难想: dp0[u]表示只考虑以u结点为根的子树的方案数 dp1[u]表示u结点往上走,倒过来,以它父亲为根那部分的方案数 有了这两部分的结果,对于各个点u的答案就是dp0[u]*(dp1[u]+1).这两部分求法如下,画

Codeforces 219D Choosing Capital for Treeland(树形DP)

题目是给一张边有向的树形图.要选出首都的点,首都要都能走到其他点,因此要反转一些边的方向.问可以选哪几个点作为首都,使它们所需反转边的数量最少. 这题挺好想的,因为做过HDU2196. 首先就不妨设正向边权值为0,反向边权值为1,那样就是各个点出发到其他点经过边所需的最少权值和. 然后对于每个点,分两个部分考虑:以这个点为根的子树.这个点往上走的部分: dp[0][u]表示以u点作为首都且以u点为根的子树部分所需反转边的数量,容易知道就等于子树内边权和 dp[1][u]表示以u点作为首都且u点向

POJ3162 Walking Race(树形DP+尺取法+单调队列)

题目大概是给一棵n个结点边带权的树,记结点i到其他结点最远距离为d[i],问d数组构成的这个序列中满足其中最大值与最小值的差不超过m的连续子序列最长是多长. 各个结点到其他结点的最远距离可以用树形DP解决,HDU2196. 而那个最长的连续子序列可以用单调队列求..搞了挺久看了解法体会了下..简单来说就是尺取法,用两个指针[i,j]表示区间,j不停+1往前移动,然后用两个单调队列分别同时更新区间最小值和最大值,再看两个队列队首的最值差是否大于m,是的话出队并调整i值,最后用j-i+1更新答案.

POJ 3162 Walking Race 树形DP+线段树

给出一棵树,编号为1~n,给出数m 漂亮mm连续n天锻炼身体,每天会以节点i为起点,走到离i最远距离的节点 走了n天之后,mm想到知道自己这n天的锻炼效果 于是mm把这n天每一天走的距离记录在一起,成为一段长度为n的数列 现在mm想要从这数列中选出一个连续的区间,要求这个区间的max-min<=m 输出最长的区间 做了一个下午 思路: 分成2个部分: 1.求出数列,即对于一棵树,求出每一个节点能到达的最远距离 2.对于这段数列,选出一个区间,使得区间的max-min<=m,并且使得区间长度尽量