1. 程式人生 > >樹形依賴揹包

樹形依賴揹包

問題大意:給出一棵樹,根節點為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在模擬賽中提到這個知識點。