ARC096F Sweet Alchemy

本文介绍了一种优化的背包问题解决方案,通过调整贪心策略并结合多重背包方法来解决特定的树形结构问题。该方法首先对物品进行特殊处理转换为背包问题,并针对物品的价值密度进行排序,最终实现对复杂限制条件下的最优解求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ARC096F

一棵树,每个节点有个权值 m i m_i mi

你要给每个点钦定一个非负整数 c i c_i ci,要求: c f a i ≤ c i ≤ c f a i + d c_{fa_{i}}\le c_i\le c_{fa_{i}}+d cfaicicfai+d

并且满足 ∑ m i c i ≤ x \sum m_ic_i\le x micix

最大化 ∑ c i \sum c_i ci

n ≤ 50 n\le 50 n50

x , d ≤ 1 0 9 x,d\le 10^9 x,d109


显而易见首先差分一下,就变成了一个背包问题:物品的代价为子树中 m i m_i mi的和,价值为子树大小。每个物品可以取最多 d d d个(除了根节点之外)。

现在记价值为 v i v_i vi,代价为 w i w_i wi

有个十分错误的贪心:以 v i w i \frac{v_i}{w_i} wivi从大到小排序,然后贪心地选取。

考虑如何调整这个贪心做法:假如有 v i w i > v j w j \frac{v_i}{w_i}>\frac{v_j}{w_j} wivi>wjvj,如果选超过 v i v_i vi j j j,自然不如选 v j v_j vj i i i(如果这个时候还有 v j v_j vj i i i)。这时候就可以调整一下。

于是每个物品个数就取 min ⁡ ( n , d ) \min(n,d) min(n,d)个做多重背包,然后剩下的贪心即可。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 55
#define ll long long
#define INF 1000000000
int n,x,d;
int p[N];
ll w[N],v[N];
ll f[N*N*N];
int q[N];
bool cmpq(int a,int b){return (ll)v[a]*w[b]>(ll)v[b]*w[a];}
int main(){
//	freopen("in.txt","r",stdin);
	scanf("%d%d%d%lld",&n,&x,&d,&w[1]),v[1]=1;
	for (int i=2;i<=n;++i)
		scanf("%lld%d",&w[i],&p[i]),v[i]=1;
	for (int i=n;i>=2;--i){
		w[p[i]]+=w[i];
		v[p[i]]+=v[i];
	}
	int c=min(n,d);
	memset(f,63,sizeof f);
	f[0]=0;
	for (int i=1;i<=n;++i){
		ll w_=w[i],v_=v[i];
		int r=c;
		for (int lg=0;r;++lg){
			w_=w[i]*min(1<<lg,r);
			v_=v[i]*min(1<<lg,r);
			r-=min(1<<lg,r);
			for (int k=n*c*i;k>=v_;--k)
				f[k]=min(f[k],f[k-v_]+w_);
		}
	}
	d-=c;
	for (int i=1;i<=n;++i)
		q[i]=i;
	sort(q+1,q+n+1,cmpq);
	ll ans=0;
	for (int j=0;j<=n*n*n;++j){
		if (f[j]>x) continue;
		ll r=x-f[j],s=j;
		for (int i=1;i<=n;++i){
			ll t=min(r/w[q[i]],q[i]==1?(ll)INF:d);
			r-=w[q[i]]*t;
			s+=v[q[i]]*t;
		}
//		printf("%d %lld %lld\n",j,f[j],s);
		ans=max(ans,s);
	}
	printf("%lld\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值