CF19B Checkout Assistant 题解(dp好题,附推导过程)

题意

Bob的购物推车里有 n n n 个物品,每个物品的价格是 c i c_i ci,会占用服务员 t i t_i ti 的时间进行结账。Bob是个坏孩子,在服务员被某个物品占用的期间,他每秒都可以偷走一个东西。请问他最少花多少钱就可以拿走所有的东西(偷走也行)。
注意:偷一个东西需要的时间是 1 1 1 秒,一个东西占用服务员的时间是 t i t_i ti 秒,别搞混了。

思路推导

什么?!偷一个东西和买一个东西的时间居然还不一样?完全糊涂了。只能找找他们之间的关系。记买的物品的编号集合为 M M M,则偷的物品就是剩下的 n − ∣ M ∣ n-|M| nM 个(这个像绝对值一样的符号是求集合中元素个数的意思)。由于推导过程较长,非蒟蒻只需要看三个不等式,忽略文字。
M M M 中的所有物品会占用服务员 ∑ i ∈ M   t i \sum_{i \in M} \ t_i iM ti 秒,偷剩下的东西要 n − ∣ M ∣ n-|M| nM 秒,显然, n − ∣ M ∣ ≤ ∑ i ∈ M   t i n-|M| \le \sum_{i \in M} \ t_i nMiM ti,因为服务员被占用的时候才能偷东西。移项后得 ∑ i ∈ M   t i + ∣ M ∣ ≥ n \sum_{i \in M} \ t_i+|M| \ge n iM ti+Mn,不等号左边的式子是 ∣ M ∣ |M| M t i t_i ti 相加后再加上 ∣ M ∣ |M| M,可以把 ∣ M ∣ |M| M 放到 ∑ \sum 里面,式子变成 ∑ i ∈ M   ( t i + 1 ) ≥ n \sum_{i \in M} \ (t_i+1) \ge n iM (ti+1)n
看不懂的别慌,我还有一种解释:对于每一个花钱购买的物品,Bob不仅可以获得这个物品本身,还可以偷走 t i t_i ti个物品。也就是说,买一个物品 i i i,可以拿走 t i + 1 t_i+1 ti+1个物品,由于所有物品都要拿走,就得到了上面的最后一个不等式。

转化为01背包

这是一个变形的01背包,由上面推导可知, t i + 1 t_i+1 ti+1是每个物品的体积,需要保证体积之和 ≥ n ≥n n c i c_i ci显然就是每个物品的价值,只不过这里要少花钱,价值之和要尽量小。
(↑我打公式太累了)具体详见代码。

代码护送

#include<bits/stdc++.h>
using namespace std;
int n,t[2005],c[2005],maxx;
long long f[2005][4005],ans=0x3f3f3f3f3f3f3f3f;
//一定要开long long,ans的初始值也要设为0x3f3f3f3f3f3f3f3f 
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>t[i]>>c[i];
		t[i]++;//转化成普通01背包,需要加1 
		maxx=max(maxx,t[i]);
	}
	memset(f,0x3f,sizeof(f));//因为要算最小值,初始值莫忘 
	f[0][0]=0;
	for(int i=1;i<=n;i++)
		for(int j=0;j<n+maxx;j++){//从0开始继承上一层的值,j<t[i]也要继承
			f[i][j]=f[i-1][j];
			if(j>=t[i]) f[i][j]=min(f[i][j],f[i-1][j-t[i]]+c[i]);
		}
	for(int i=n;i<n+maxx;i++) ans=min(ans,f[n][i]);
	cout<<ans<<endl;
	return 0;
}

看懂了的留个赞呗(doge

  • 38
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值