[ BZOJ 4247 ] 挂饰
\(\\\)
\(Description\)
\(N\)个物品,每个物品有两个属性\(w_i\)和\(v_i\),代表价值和所能增加背包的容量,默认每一个物品体积均为\(1\),并且背包开始容量为\(1\),求合法状态下所能得到做多价值。
- \(N\in [1,2000]\),\(w_i\in [-10^6,10^6]\),\(v_i\in [1,N]\)
\(\\\)
\(Solution\)
-
首先将自己所用的那一个体积从自己的增加容量里扣掉,因为先用一个别人的容量再贡献出一个是合法的,所以可以直接扣除在自己的容量增量里。
-
设\(f[i]\)表示还剩下的容量为\(i\)时,所能得到的最大价值,然后就是普通的\(01\)背包了。
-
注意到物品最多只有\(2000\)个,而总的可能带来的空间和可能会非常大,而有用的时候容量最多只需要\(2000\),所以转移的时候空间要取\(min\)。
-
注意到如果物品随意顺序做背包,那么下标为负可能也会有意义,可以等待后面的物品补上,所以应将物品按容量贡献排序处理,这样即不能出现所谓“替换位置”以补上空间的情况。
-
注意如果不会带来新的容量,那么这个物品的增量是\(-1\)根据\(01\)背包的设计原则,不能使用当前物品更新当前物品,所以此处应特判反向转移。
\(\\\)
\(Code\)
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 2010
#define R register
#define gc getchar
#define inf 2100000000
using namespace std;
inline int rd(){
int x=0; bool f=0; char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
}
int n,ans,f[N];
struct sub{int v,num;}s[N];
inline bool cmp(sub x,sub y){return x.num>y.num;}
int main(){
n=rd();
for(R int i=1;i<=n;++i){s[i].num=rd()-1;s[i].v=rd();}
sort(s+1,s+1+n,cmp);
for(R int i=2;i<=n;++i) f[i]=-inf;
for(R int i=1;i<=n;++i){
if(s[i].num!=-1) for(R int j=n;j;--j) f[min(j+s[i].num,n)]=max(f[min(j+s[i].num,n)],f[j]+s[i].v);
else for(R int j=1;j<=n;++j) f[j-1]=max(f[j-1],f[j]+s[i].v);
for(R int i=0;i<=n;++i) ans=max(ans,f[i]);
}
for(R int i=0;i<=n;++i) ans=max(ans,f[i]);
printf("%d\n",ans);
return 0;
}