【解题报告】CF815C

【解题报告】CF815C

题目大意

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

输入第一行两个整数,\(n\)\(b\)

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

字母意义皆如题目所示

思路

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

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

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

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

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

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

然后想动态转移方程

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

我们可以有

\[f[x][i+j][0]=min(f[x][i+j][0],f[x][i][0]+f[son][j][0]) \]

这个的意思呢就是x节点有 \(i+j\) 个节点,然后选除了某个子树外的 \(i\)​ 个节点的买的数量和买这某个子树的 \(j\) 个节点的总的可以买的

\[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写完了

代码

#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;
}
posted @ 2021-10-03 19:29  wweiyi  阅读(13)  评论(0编辑  收藏  举报
js脚本