[USACO22DEC] Bribing Friends G

洛谷[USACO22DEC] Bribing Friends G

题目大意

B B B n n n个朋友,每个朋友有一个受欢迎度 p i p_i pi。小 B B B想邀请部分朋友和他去看电影,且想要最大化这些朋友的受欢迎程度之和。对于朋友 i i i,只有当小 B B B给他 c i c_i ci元钱,他才会答应去。如果小 B B B给他 x i x_i xi个冰激凌,他还可以给小 B B B一元的折扣。小 B B B可以从朋友那里得到任意整数数量的折扣,只要不是这些朋友倒贴他。

B B B a a a元钱和 b b b个冰激凌,求他在采用最佳策略的情况下,可以达到的最大的受欢迎程度之和。

1 ≤ n ≤ 2000 , 0 ≤ a , b ≤ 2000 1\leq n\leq 2000,0\leq a,b\leq 2000 1n2000,0a,b2000


题解

将朋友根据 x i x_i xi从小到大排序,那么显然冰激凌对前面的朋友用,钱对后面的朋友用才更优。而且,既用了冰激凌又用了钱的朋友最多只有一个,因为如果有两个,则将后一个的冰激凌用在前一个,前一个的钱用在后一个,这样能使要用的冰激凌数量更少。

用两个背包来处理。设 f i , j f_{i,j} fi,j表示前 i i i个朋友中花费 j j j个冰激凌能邀请到朋友的最大欢迎度, g i , j g_{i,j} gi,j表示第 i i i个朋友到第 n n n个朋友中花费 j j j元钱能邀请到朋友的最大欢迎度。那么

  • 如果没有既用了冰激凌又用了钱的朋友,则枚举最后一个只用了冰激凌的朋友,更新答案
  • 如果有既用了冰激凌又用了钱的朋友,则枚举这个朋友,并枚举这个朋友所花费的冰激凌和钱,更新答案

时间复杂度为 O ( n ( a + b ) ) O(n(a+b)) O(n(a+b))

code

#include<bits/stdc++.h>
using namespace std;
int n,v1,v2,ans,f[2005][2005],g[2005][2005];
struct node{
	int p,c,x;
}w[2005];
bool cmp(node ax,node bx){
	return ax.x<bx.x;
}
int main()
{
	scanf("%d%d%d",&n,&v1,&v2);
	for(int i=1;i<=n;i++){
		scanf("%d%d%d",&w[i].p,&w[i].c,&w[i].x);
	}
	sort(w+1,w+n+1,cmp);
	for(int i=1;i<=n;i++){
		for(int j=0;j<=v2;j++){
			f[i][j]=f[i-1][j];
			if(j>=w[i].c*w[i].x)
			f[i][j]=max(f[i][j],f[i-1][j-w[i].c*w[i].x]+w[i].p);
		}
	}
	for(int i=n;i>=1;i--){
		for(int j=0;j<=v1;j++){
			g[i][j]=g[i+1][j];
			if(j>=w[i].c) g[i][j]=max(g[i][j],g[i+1][j-w[i].c]+w[i].p);
		}
	}
	ans=g[1][v1];
	for(int i=1;i<=n;i++){
		ans=max(ans,f[i][v2]+g[i+1][v1]);
		for(int j=v2,k=0;j>=0&&k<=w[i].c;j-=w[i].x,k++){
			if(v1-w[i].c+k>=0)
			ans=max(ans,f[i-1][j]+g[i+1][v1-w[i].c+k]+w[i].p);
		}
	}
	printf("%d",ans);
	return 0;
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值