树形依赖揹包

问题大意:给出一棵树,根节点为1,每个点有毒素和收获。要求毒素不超过给定值的情况下使收获最大。一个点的父亲节点被选取后这个点才能被选取。

首先弄出dfs序,也记录下每个点其子树及自身的大小。每个点都能够被选或不选,如果选了才会考虑它子树。
f[i][j] 表示dfs序上第i位上的点在其子树及自身上选取了毒素和为j的点所能获得的最大收益。(下面x指dfs序上第i位代表的点)

如果 j<cost[x] f[i][j]=f[i+size[x]][j] 。代表这个点及其子树都不能选( +size[x] 代表在dfs序上跳过这个点的子树)
如果 jcost[x] f[i][j]=max(f[i+1][jc[x]]+v[x],f[i+size[x]][j])
注意是否会出现负收益,并选择是否需要再与0比较。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std ;
bool Read ( int &x, char c = getchar(), bool flag = false ) {
    for ( x = 0 ; !isdigit(c) ; c = getchar() ) if ( c == EOF ) return false ; else if ( c == '-' ) flag = true ;
    for ( ; isdigit(c) ; c = getchar() ) x = 10*x + c - '0' ; if ( flag ) x = -x ;
    return true ;
}
const int maxn = 5010, maxm = maxn<<1, zhf = 1<<28 ;
int ans, n, m, e, be[maxn], to[maxm], nxt[maxm], v[maxn], c[maxn], dfn[maxn], rdn[maxn], clk, size[maxn], f[maxn][maxn] ;
void add ( int x, int y ) {
    to[++e] = y ;
    nxt[e] = be[x] ;
    be[x] = e ;
}
void dfs ( int x, int fa ) {
    ( dfn[x] = ++clk )[rdn] = x ;
    size[x] = 1 ;
    int i, u ;
    for ( i = be[x] ; i ; i = nxt[i] ) {
        u = to[i] ;
        if ( u == fa ) continue ;
        dfs(u,x) ;
        size[x] += size[u] ;
    }
}
int main() {
    int i, j, k, x, y, z ;
    Read(n) ; Read(m) ;
    for ( i = 1 ; i <= n ; i ++ ) {
        Read(v[i]) ;
        Read(c[i]) ;
    }
    for ( i = 1 ; i < n ; i ++ ) {
        Read(x) ; Read(y) ;
        add ( x, y ) ;
        add ( y, x ) ;
    }
    dfs(1,1) ;
    for ( i = n ; i ; i -- ) {
        x = rdn[i] ;
        for ( j = 0 ; j <= m ; j ++ )
            if ( j < c[x] ) f[i][j] = max ( f[i+size[x]][j], 0 ) ;
            else f[i][j] = max ( max( f[i+1][j-c[x]]+v[x], f[i+size[x]][j] ), 0 ) ;
    }
    printf ( "%d\n", f[1][m] ) ;
    return 0 ;
}

感谢Wearry在模拟赛中提到这个知识点。

点赞