1. 程式人生 > >$[ BZOJ 4247 ] $ 掛飾

$[ BZOJ 4247 ] $ 掛飾

register 別人 等待 get 1+n zoj 開始 def return


\(\\\)

\(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;
}

$[\ BZOJ\ 4247\ ]\ $ 掛飾