题意:给出一颗树,树根有无穷大的能量。所有的就节点通过管道连接,管道能流过的能量上限为p[i](1-100)。点亮每一个节点都需要一定的能量r[i](0-100)。流入每个子节点管道的能量总和,不能超过它与父亲相连的管道的直径。问最多能点亮几个节点。
解题思路:题目要求最优值,可以想到dp的思路。dp[u][i]表示流入u节点i 的能量,最多能将u子树上的多少个节点点亮。那么就转换成了一个分组背包问题,即每一个儿子为一个组别,而每一组内有若干个物品,其体积不会超过管道的直径,且每一组仅能取一个或零个物品。因为仅能取其中一个物品,所以dp用了滚动数组,避免用本组的dp值跟新本组的dp值。即dp[u][i][0]为本组之前所有组跟新得到的dp值,dp[u][i][1]为用本组物品跟得到的背包值。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std ;
int max ( int a , int b ) { return a > b ? a : b ; }
struct Edge
{
int t , next , v ;
} edge[11111];
int head[1111] , tot , dp[1111][111][2] , r[1111] ;
void new_edge ( int a , int b , int c )
{
edge[tot].t = b ;
edge[tot].v = c ;
edge[tot].next = head[a] ;
head[a] = tot ++ ;
}
void init ( int n )
{
int i , j ;
tot = 0 ;
for ( i = 0 ; i <= n ; i ++ )
for ( j = 0 ; j <= 100 ; j ++ )
dp[i][j][0] = dp[i][j][1] = 0 ;
for ( i = 0 ; i <= n ; i ++ ) head[i] = -1 ;
}
void dfs ( int u , int mx )
{
if ( head[u] == -1 )
{
if ( r[u] <= mx ) dp[u][r[u]][1] = 1 ;
return ;
}
int i , j , k ;
for ( i = head[u] ; i != -1 ; i = edge[i].next )
dfs ( edge[i].t , edge[i].v ) ;
for ( j = head[u] ; j != -1 ; j = edge[j].next )
{
int v = edge[j].t ;
for ( k = 0 ; k <= edge[j].v ; k ++ )
for ( i = mx ; i >= k ; i -- )
dp[u][i][1] = max ( dp[u][i][1] , dp[u][i-k][0] + dp[v][k][1] ) ;
for ( k = 0 ; k <= edge[j].v ; k ++ )
for ( i = mx ; i >= k ; i -- )
dp[u][i][0] = dp[u][i][1] ;
}
for ( i = mx ; i >= r[u] ; i -- )
dp[u][i][1] = max ( dp[u][i][1] , dp[u][i-r[u]][1] + 1 ) ;
}
int main ()
{
int n , i , a , b , c , j ;
while ( scanf ( "%d" , &n ) != EOF )
{
init ( n ) ;
for ( i = 1 ; i <= n ; i ++ )
{
scanf ( "%d%d%d" , &a , &b , &c ) ;
new_edge ( a , i , c ) ;
r[i] = b ;
}
int sum = 0 ;
for ( i = head[0] ; i != -1 ; i = edge[i].next )
{
int v = edge[i].t ;
dfs ( v , edge[i].v ) ;
int add = 0 ;
for ( j = 0 ; j <= edge[i].v ; j ++ )
add = max ( add , dp[v][j][1] ) ;
sum += add ;
}
printf ( "%d\n" , sum ) ;
}
}
/*
3
0 0 2
0 0 100
1 0 1
*/