【解题报告】CF815C

题目大意

Kellen想要买 nn 件东西,她有 bb 块钱,实际上,超市也就只有 nn 件东西,超市为Kellen准备了 nn 种优惠券,但是对于除第一件商品以外的优惠券,必须在使用第 xix_i 张优惠券之后方才可以使用,你能帮她了解一下在不超过她手上的钱的情况下,她最多能买多少东西

输入第一行两个整数,nnbb

从第二行到第 n+1n+1 行,除第二行前两个整数之外,后面的每行三个整数 $c_i \space,d_i \space $ 和 xix_i

字母意义皆如题目所示

思路

这道题目,我们可以从必须使用 xix_i 之后才可以使用 ii 知道,这个类似于没有上司的舞会,或者是选课,因此我们要使用树形DP

按照之前的惯例,我们可以设置如下状态

设置 f[i][j][0/1]f[i][j][0/1] 表示在以 ii 为根的子树中,选择 jj​ 个物品,在第 $i $ 个商品上面是否使用优惠券可以最多购买东西的数量

然后我们根据定义,我们可以列出如下初始状态

  • xx​ 个节点的子树不购买,他自己也不购买,不使用优惠券,没有代价
  • xx 个节点的子树不购买,购买他自己,但是也不使用优惠券,代价是买 xx 花的钱 cxc_x
  • xx 个节点地字数不购买,购买他自己,使用优惠券,这样他们的代价就是 cxdic_x-d_i

所以我们的初始状态就定义完了

然后想动态转移方程

怎么想呢,枚举自己的大小和子树和他所有儿子的大小

我们可以有
f[x][i+j][0]=min(f[x][i+j][0],f[x][i][0]+f[son][j][0]) f[x][i+j][0]=min(f[x][i+j][0],f[x][i][0]+f[son][j][0])
这个的意思呢就是x节点有 i+ji+j 个节点,然后选除了某个子树外的 ii​ 个节点的买的数量和买这某个子树的 jj 个节点的总的可以买的
f[x][i+j][1]=min{f[x][i][1]+f[son][j][0]f[x][i][1]+f[son][j][1] f[x][i+j][1]= min \begin{cases} f[x][i][1]+f[son][j][0] \\ f[x][i][1]+f[son][j][1] \end{cases}
这个方程的意思和上面的差不多,也就不做过多解释了

因此我们对着是输入的数据疯狂建树,然后将自己的边指向需要自己的节点,然后我们跑一边 dfs 就可以把树形DP写完了

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
const int maxn=5005;
int n,b,ans;
int c[maxn],d[maxn];
int x[maxn];

int size[maxn],f[maxn][maxn][5];

struct edge{
int e,next;
}ed[maxn<<1];
int en,first[maxn];
void add_edge(int s,int e)
{
en++;
ed[en].next=first[s];
first[s]=en;
ed[en].e=e;
}
void dfs(int x)
{
size[x]=1;
f[x][0][0]=0;
f[x][1][0]=c[x];
f[x][1][1]=c[x]-d[x];
for(int i=first[x];i;i=ed[i].next)
{
int e=ed[i].e;
dfs(e);
for(int i=size[x];i>=0;i--)
{
for(int j=0;j<=size[e];j++)
{
f[x][i+j][0]=min(f[x][i+j][0],f[x][i][0]+f[e][j][0]);
f[x][i+j][1]=min(f[x][i+j][1],min(f[x][i][1]+f[e][j][0],f[x][i][1]+f[e][j][1]));
}
}
size[x]+=size[e];
}
}
int main()
{
memset(f,0x3f3f3f,sizeof(f));
cin>>n>>b;
cin>>c[1]>>d[1];
for(int i=2;i<=n;i++)
{
cin>>c[i]>>d[i]>>x[i];
add_edge(x[i],i);
}
dfs(1);
for(int i=n;i>=1;i--)
{
if(f[1][i][0]<=b||f[1][i][1]<=b)
{
ans=i;
break;
}
}
cout<<ans<<'\n';
return 0;
}