题目地址:http://poj.org/problem?id=1155
基础的树形dp,在以i为根结点的子树上选择儿子的过程就是做一次0-1背包,背包体积变化,物品体积变化。
dp[i][j]表示以i为根结点的子树选择j个用户的最大代价,转移方程为dp[i][j] = max(dp[i][j],dp[i][j-k]+dp[son[i]][k]);
#include <stdio.h>
#include <vector>
using namespace std;
const int MAXN = 3005;
const int INF = 0x3f3f3f3f;
struct node
{
int end,val;
};
vector<node> v[MAXN];
int cost[MAXN],dp[MAXN][MAXN];
int n,m;
inline node make_node(int end,int val)
{
node a;
a.end = end;a.val = val;
return a;
}
inline int max(int a,int b){ return a < b ? b : a; }
int dfs(int root)
{
int sum = 1;
vector<node>::iterator it;
for(it=v[root].begin();it!=v[root].end();it++)
{
sum += dfs((*it).end);
}
for(it=v[root].begin();it!=v[root].end();it++)
for(int j = sum;j >= 0;j--)
for(int k = 0;k <= j;k++)
dp[root][j] = max(dp[root][j],dp[(*it).end][k]+dp[root][j-k]-(*it).val);
//printf("root = %d sum = %d\n",root,sum);
return sum;
}
int main()
{
int num,a,b;
while(scanf("%d%d",&n,&m) == 2)
{
for(int i = 1;i <= n;i++) v[i].clear();
for(int i = 1;i <= n-m;i++)
{
scanf("%d",&num);
for(int j = 1;j <= num;j++)
{
scanf("%d%d",&a,&b);
v[i].push_back(make_node(a,b));
}
}
for(int i = n-m+1;i <= n;i++) scanf("%d",&cost[i]);
for(int i = 1;i <= n;i++)
for(int j = 0;j <= n;j++) dp[i][j] = -INF;
for(int i = 1;i <= n;i++) dp[i][0] = 0;
for(int i = n-m+1;i <= n;i++) dp[i][1] = cost[i];
dfs(1);
bool find = 0;
for(int i = n;i >= 1;i--){
if(dp[1][i] >= 0){
find = 1;
printf("%d\n",i);
break;
}
}
if(!find) printf("0\n");
}
}