[背包]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;
}