题意:有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 ",i); 54 break; 55 } 56 } 57 return 0; 58 }