zstu4119 Juice树形dp

题意:给出一颗树,树根有无穷大的能量。所有的就节点通过管道连接,管道能流过的能量上限为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
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值