部分贪心解决大背包问题
先利用数据特殊性将物品压缩成18个,第i个体积是i,重量为w[i],这里w[i]取体积为i的原物品的最大值。
然后找到性价比最大的wi,vi。
考虑任意一个物品p,若取q个该物品且q>=vi,即q=vi+x(x>=0),则总重量为w[p]*(vi+x)
w[p]*(vi+x)=w[p]*vi+w[p]*x<wi*vi+w[p]*x。于是对于任意性价比不为wi,vi的物品,qi<vi。
所以取limit=vi*18,即每个物品都取18个,则容量为k时的最优解可以表示为
令t=(k-limit)/vi(上取整,原论文的下取整不对)
weight(k)=t*wi+f[k-t*vi]。
f可以先DP出来。
然后再做一遍背包,即对合并进行DP,此时令w[i]=weight(i*y)-(i-1)*c,v[i]=i,论文中S为10^9,再次套用刚才的方法求解。
当然题目中S<=1000,可以直接求解。
#include<iostream> #include<cstring> #include<cstdio> using namespace std; typedef long long ll; int wi,vi,lim; ll f[1005],w[405]; ll weight(ll k){ ll t=(k-lim)/vi; if((k-lim)%vi)t++; return t*wi+f[k-t*vi]; } int main(){ //freopen("a.in","r",stdin); //freopen("a.out","w",stdout); int T;scanf("%d",&T); while(T--){ int s,y,n,c; scanf("%d%d%d%d",&n,&s,&y,&c); memset(w,0,sizeof(w)); for(int i=1;i<=n;i++){ int a,b;scanf("%d%d",&a,&b); w[b]=max(w[b],(ll)a); } wi=w[1],vi=1; for(int i=1;i<=18;i++) if(w[i]*vi>wi*i)wi=w[i],vi=i; lim=18*vi; memset(f,0,sizeof(f)); for(int i=1;i<=18;i++) for(int j=i;j<=lim;j++) f[j]=max(f[j],f[j-i]+w[i]); ll t=(y-lim)/vi; for(int i=1;i<=lim;i++)f[i]=max(f[i],f[i-1]); for(int i=1;i<=lim;i++) w[i]=weight((ll)y*i)-c*(i-1); memset(f,0,sizeof(f)); for(int i=1;i<=lim;i++) for(int j=i;j<=s;j++) f[j]=max(f[j],f[j-i]+w[i]); printf("%lld\n",f[s]); } return 0; }