[ 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;
}

posted @ 2018-09-09 16:39  SGCollin  阅读(78)  评论(0编辑  收藏  举报