Tree DP 总结

一些说在前面的默认设定:

x当前节点
c当前节点的儿子节点
f当前节点的父亲节点
(x,y)x->y的路径
wx,yx->y的路径长度

Example 1

description:给定一个无向图G,求此图中任意两点之间路径的最短路的最大值。
情形一:普通图
对于每个节点都跑一遍spfa or floyd.
情形二:树形图
算法1:贪心
对于任意一个节点x,找到离其最远的点y,再从y找到离其最远的点z,dist(y,z)即为答案。
算法2:树形dp
设1为根,则该图变为有根树,记L[x]为以x为根的子树中的最长路。显然L[1]即为答案。
分类讨论——两种路径
1. 不经过节点x的路径:
max L[c]
2. 经过节点x的路径:
down[x]x向下能走的最长路的长度,down[x]=max(down[c]+wc>x)
L[x]down[c]中的最大值和次大值之和(这里次大值的初始值为0)

Example 2

description:给定一个无向图G,求此图中从每个点开始的最长路。
分类讨论
第一步向下走:即down[x]
第一步向上走:设up[x]为以x为起点,第一步往上走能走的最长路的长度.
up[x]求法:
第一步从x走到了f
第二步继续向上走:即up[f]
第二步向下走(notice:不能回到x):如果如果down[f] 不是由down[x] 更新得来,那么就是down[f],如果是的话,就是down2[f].
down2[x]=smax(down[c]+wc>x)
down1v[x] 记录down[x]是由哪一个c转移过来的。

/*
  ID:Agreement
  PROG:HDU 2196 Computer
*/
// Invincible
#include <bits/stdc++.h>
#define rep( i , l , r ) for( int i = (l) ; i <= (r) ; ++i )
#define per( i , r , l ) for( int i = (r) ; i >= (l) ; --i )
#define erep( i , u ) for( int i = head[(u)] ; ~i ; i = e[i].nxt )
using namespace std;
const int maxn = 10000 + 5;
struct edge{
    int v , w , nxt;
}e[maxn * 2];
int head[maxn] , _t;
inline void ins( int u , int v , int w ){
    e[_t].v = v , e[_t].w = w , e[_t].nxt = head[u] , head[u] = _t; _t++;
    e[_t].v = u , e[_t].w = w , e[_t].nxt = head[v] , head[v] = _t; _t++; 
}
int down[maxn] , down2[maxn] , down1v[maxn] , up[maxn];
void dfs1( int u , int f ){
    erep( i , u ){
        int v = e[i].v;
        if( v == f ) continue;
        dfs1( v , u );
        if( down[u] < down[v] + e[i].w ){
            down2[u] = down[u];
            down[u] = down[v] + e[i].w;
            down1v[u] = v;
        }else{
            down2[u] = max( down2[u] , down[v] + e[i].w );
        }
    }
} 
void dfs2( int u , int f ){
    erep( i , u ){
        int v = e[i].v;
        if( v == f ) continue;
        if( down1v[u] != v ) up[v] = max( up[u] , down[u] ) + e[i].w;
        else up[v] = max( up[u] , down2[u] ) + e[i].w;
        dfs2( v , u );  
    }
}
void clear(){
    _t = 0;
    memset( head , 0xff , sizeof head );
    memset( up , 0 , sizeof up );
    memset( down , 0 , sizeof down );
    memset( down2 , 0 , sizeof down2 );
}
int main(){
    int N = 0 , u , v , w;
    while( scanf("%d" , &N) != EOF && N ){
        clear();
        rep( i , 2 , N ){
            scanf("%d %d" , &v , &w);
            ins( i , v , w );
        }
        dfs1( 1 , -1 );
        dfs2( 1 , -1 );
        rep( i , 1 , N )
            printf( "%d\n" , max( down[i] , up[i] ) ); 
    }
    return 0;
}

Example 3

SPOJ MTREE

在一棵树上,求出所有路径边权积的和取模。

题解:由于本题无特殊限制,将任意一点设为根,这样任意一条路径由一到两条儿子->祖先这样的路径组成,如果两条路径有重叠可以乘法分配律提取公因数,转化到树上即是记录每一个子树内的答案以及包含子树边路径的答案。

Example 4

SPOJ GS

给定一棵树,每个点都有给定概率往其相邻点走,问从某一点到另一点期望走多少步。

没有找到原题,不知道是否可以绕环走,这样有可能无法出答案,所以暂时不写题解了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值