[背包]CF19B Checkout Assistant 题解

背包好题,也包含了一种思想。

题意

有若干物品,每个物品有两个属性 \(a_i\) , \(b_i\) ,表示这个物品的费用 \(a_i\) 以及如果选择了这个物品我可以白嫖 \(b_i\) 个物品。最小的费用数。

主要思想

如果我们正向考虑,似乎十分难想到一个贪心策略。就算我现在知道了我们现在有选择 \(j\) 个物品的机会,我们也不知道选择给谁这个机会,所以说我们似乎不能从正面开始考虑。

正确的思路是将选择 \(j\) 个物品的机会想成是一个容量为 \(j+1\) 的物品,为什么?因为我们可以想到,如果我们有选择 \(j\) 个物品的机会,我们可以记在那里,不去想选哪 \(j\) 个物品最好,而是看看我选完这 \(j\) 个物品之后选什么。那么如果我把它想成一个 \(j+1\) 的物品,我只要到容量大于等于 \(n\) 就行了。

这样的话我们的话就可以转成一个 01 背包的问题了。记得要开 long long 。

并且学到了一个新的东西, memset 对于 long long 的数组初始化也会初始化到 long long 可以承受的最大值的。

代码

#include <bits/stdc++.h>
#define debug puts("I love Newhanser forever!!!!!");
#define pb push_back
using namespace std;
template <typename T>inline void read(T& t){
    t=0; register char ch=getchar(); register int fflag=1;
    while(!('0'<=ch&&ch<='9')){if(ch=='-') fflag=-1;ch=getchar();}
    while(('0'<=ch&&ch<='9')){t=t*10+ch-'0'; ch=getchar();} t*=fflag;
}
const int MAXN=5086;
typedef long long ll;
int n,V,v[MAXN];
ll dp[MAXN],c[MAXN];
int main(){
	read(n);
	for(int i=1;i<=n;++i){
		read(v[i]);
		v[i]++;
		read(c[i]);
		V=max(v[i],V); 
	}
	V+=n;
	memset(dp,0x3f,sizeof(dp));
	dp[0]=0;
	for(int i=1;i<=n;++i)
		for(int j=V;j>=v[i];--j)
			dp[j]=min(dp[j-v[i]]+c[i],dp[j]);
	ll ans=2e12;
	for(int j=n;j<=V;++j) ans=min(ans,dp[j]);
	cout<<ans<<endl;
    return 0;
}
posted @ 2022-07-10 20:40  Mercury_City  阅读(36)  评论(0编辑  收藏  举报