题意:有n件商品,每件商品都最多只能被买一次,且有一个原价和一个如果使用优惠券以后可以减少的价格,同时,除了第一件商品以外每件商品都有一个xi属性,表示买这个商品时如果要使用优惠券必须已经使用了xi的优惠券。现在有B的钱,问在不超过B的钱的情况下最多能买多少件商品。

  做法:因为根据x属性,所有商品能够被连缀成一棵以1为根节点的树,因此考虑树形dp,因为n=5000,所以定义状态如下:dp[i][j][p],表示从i这件开始买,已经买了j件商品且i这件商品是否已经使用了优惠券(1表示使用,0表示没有)的最小花费。要注意的是,由于每件商品最多只能被购买一次,因此在dfs里面枚举i和j的时候需要从大到小(和01背包一个道理)。代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int N = 5000 + 5;
 5 const int mod = 1e9 + 7;
 6 typedef pair<int,int> pii;
 7 
 8 int n, money;
 9 vector<int> G[N];
10 int a[N], b[N];
11 ll dp[N][N][2]; // from i, buy j already, 0 -> not use quan
12 int sz[N];
13 void update(ll &x, ll y) {if(x > y) x = y;}
14 void dfs(int u)
15 {
16     dp[u][0][0] = 0;
17     dp[u][1][0] = a[u];
18     dp[u][1][1] = a[u] - b[u];
19     sz[u] = 1;
20     for(int v: G[u])
21     {
22         dfs(v);
23         for(int i=sz[u];i>=0;i--)
24         {
25             for(int j=sz[v];j>=0;j--)
26             {
27                 update(dp[u][i+j][0], dp[v][j][0] + dp[u][i][0]);
28                 update(dp[u][i+j][1], min(dp[v][j][0], dp[v][j][1]) + dp[u][i][1]);
29             }
30         }
31         sz[u] += sz[v];
32     }
33 }
34 
35 int main()
36 {
37     cin >> n >> money;
38     for(int v=1;v<=n;v++)
39     {
40         scanf("%d%d",a+v,b+v);
41         if(v > 1)
42         {
43             int u; scanf("%d",&u);
44             G[u].push_back(v);
45         }
46     }
47     memset(dp,0x3f,sizeof dp);
48     dfs(1);
49     for(int i=n;i>=0;i--)
50     {
51         if(dp[1][i][0] <= money || dp[1][i][1] <= money)
52         {
53             printf("%d\n",i);
54             break;
55         }
56     }
57     return 0;
58 }