原题链接

考察:树形背包dp

思路:

        首先我们需要拿一个变量作为背包的体积,不能以钱数做体积,因为范围没有给定.所以只能以人数做体积.f[i][j]表示以i为根节点的子树中,选j个人的最大花费.如果>=0表示方案可行.因为人数是可以枚举的,所以不会漏解.

       这里要注意的是背包体积不能直接设为m,否则会TLE,因f[i][j]是被优化为二维的dp,实际三维dp是f[i][j][k] 以i为根节点的子树中,前j个子节点中选k个人,明显j那一维是可以优化掉的.k的取值范围是当前子节点个数.所以dfs需要返回当前子节点个数.

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 using namespace std;
 5 const int N = 3010; 
 6 int m,n,idx,h[N],w[N],money[N],cnt[N];
 7 int f[N][N];
 8 struct Road{
 9     int fr,to,ne;
10 }road[N];
11 void add(int a,int b)
12 {
13     road[idx].fr = a,road[idx].to = b,road[idx].ne = h[a],h[a] = idx++;
14 }
15 void init()
16 {
17     scanf("%d%d",&n,&m);
18     memset(f,-0x3f,sizeof f);
19     memset(h,-1,sizeof h);
20     for(int i=1;i<=n-m;i++)
21     {
22         int k; scanf("%d",&k);
23         while(k--)
24         {
25             int s,p; scanf("%d%d",&s,&p);
26             add(i,s); w[s] = p;
27         }
28     }
29     for(int i=n-m+1;i<=n;i++) scanf("%d",&money[i]),cnt[i] = 1;
30 }
31 int dfs(int u)
32 {
33     f[u][0] = 0;
34     int sum = 1; 
35     for(int i=h[u];i!=-1;i=road[i].ne)
36     {
37         int v = road[i].to;
38         sum+=dfs(v);//不倒序会选到重复的点. 
39         for(int j=sum;j>=0;j--)//多少人 
40           for(int k=0;k<=j;k++)//子节点多少人 
41            f[u][j] = max(f[v][k]+f[u][j-k],f[u][j]);
42     }
43     for(int i=sum;i>=1;i--) 
44       f[u][i] = f[u][i-cnt[u]]-w[u]+money[u]; 
45     return sum;
46 }
47 int main()
48 {
49     init();
50     int s = dfs(1);
51     for(int i=s;i>=0;i--) 
52       if(f[1][i]>=0) {printf("%d\n",i);return 0;} 
53     return 0;
54 }