【解题报告】CF815C
题目大意
Kellen想要买 n n n 件东西,她有 b b b 块钱,实际上,超市也就只有 n n n 件东西,超市为Kellen准备了 n n n 种优惠券,但是对于除第一件商品以外的优惠券,必须在使用第 x i x_i x i 张优惠券之后方才可以使用,你能帮她了解一下在不超过她手上的钱的情况下,她最多能买多少东西
输入第一行两个整数,n n n 和 b b b
从第二行到第 n + 1 n+1 n + 1 行,除第二行前两个整数之外,后面的每行三个整数 $c_i \space,d_i \space $ 和 x i x_i x i
字母意义皆如题目所示
思路
这道题目,我们可以从必须使用 x i x_i x i 之后才可以使用 i i i 知道,这个类似于没有上司的舞会,或者是选课,因此我们要使用树形DP
按照之前的惯例,我们可以设置如下状态
设置 f [ i ] [ j ] [ 0 / 1 ] f[i][j][0/1] f [ i ] [ j ] [ 0 / 1 ] 表示在以 i i i 为根的子树中,选择 j j j 个物品,在第 $i $ 个商品上面是否使用优惠券可以最多购买东西的数量
然后我们根据定义,我们可以列出如下初始状态
第 x x x 个节点的子树不购买,他自己也不购买,不使用优惠券,没有代价
第 x x x 个节点的子树不购买,购买他自己,但是也不使用优惠券,代价是买 x x x 花的钱 c x c_x c x
第 x x x 个节点地字数不购买,购买他自己,使用优惠券,这样他们的代价就是 c x − d i c_x-d_i c x − d i
所以我们的初始状态就定义完了
然后想动态转移方程
怎么想呢,枚举自己的大小和子树和他所有儿子的大小
我们可以有
f [ x ] [ i + j ] [ 0 ] = m i n ( f [ x ] [ i + j ] [ 0 ] , f [ x ] [ i ] [ 0 ] + f [ s o n ] [ j ] [ 0 ] )
f[x][i+j][0]=min(f[x][i+j][0],f[x][i][0]+f[son][j][0])
f [ x ] [ i + j ] [ 0 ] = m i n ( f [ x ] [ i + j ] [ 0 ] , f [ x ] [ i ] [ 0 ] + f [ s o n ] [ j ] [ 0 ] )
这个的意思呢就是x节点有 i + j i+j i + j 个节点,然后选除了某个子树外的 i i i 个节点的买的数量和买这某个子树的 j j j 个节点的总的可以买的
f [ x ] [ i + j ] [ 1 ] = m i n { f [ x ] [ i ] [ 1 ] + f [ s o n ] [ j ] [ 0 ] f [ x ] [ i ] [ 1 ] + f [ s o n ] [ 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}
f [ x ] [ i + j ] [ 1 ] = m i n { f [ x ] [ i ] [ 1 ] + f [ s o n ] [ j ] [ 0 ] f [ x ] [ i ] [ 1 ] + f [ s o n ] [ j ] [ 1 ]
这个方程的意思和上面的差不多,也就不做过多解释了
因此我们对着是输入的数据疯狂建树,然后将自己的边指向需要自己的节点,然后我们跑一边 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 ; }