P1776 宝物筛选(多重背包基础)
二进制拆分代码:
#include<bits/stdc++.h>
using namespace std;
const int N=55005;
int f[N],n,V,ans=INT_MAX;
int main(){
scanf("%d%d",&n,&V);
for(int i=1;i<=V+5000;i++) f[i]=1e9;
for(int i=1,w,v;i<=n;i++){
scanf("%d%d",&w,&v);
for(int j=w;j<=V+5000;j++) f[j]=min(f[j],f[j-w]+v);
}
for(int i=V;i<=V+5000;i++) ans=min(ans,f[i]);
printf("%d\n",ans);
return 0;
}
单调队列朴素代码:
#include<bits/stdc++.h>
#define f(x) (f[i-1][(x)*w+mod]-(x)*v)
using namespace std;
const int N=103,M=40005;
int n,V,f[N][M];
//Deque
int a[M],ff,tt;
int main(){
scanf("%d%d",&n,&V);
for(int i=1,v,w,k;i<=n;i++){
scanf("%d%d%d",&v,&w,&k);
for(int mod=0;mod<w;mod++){
//这里要注意设定 f[i-1][mod] 的初始值,下一轮要用。
ff=tt=1,a[1]=0,f[i][mod]=f[i-1][mod];
for(int num=1;num*w+mod<=V;num++){
while(ff<=tt&&num-a[ff]>k) ++ff;
//当前这个物体可能非常唐,可以一个不要,要和 f[i-1][mod+num*w] 取 max。
f[i][mod+num*w]=max(f[i-1][mod+a[ff]*w]+v*(num-a[ff]),f[i-1][mod+num*w]);
while(ff<=tt&&f(num)>=f(a[tt])) --tt;
a[++tt]=num;
}
}
}
printf("%d\n",f[n][V]);
return 0;
}
压缩空间被迫在队列里存一个值进去。
这个版本能跑赢二进制优化。
#include<bits/stdc++.h>
using namespace std;
const int M=40005;
int n,V,f[M];
//Deque
int a[M],val[M],ff,tt;
int main(){
scanf("%d%d",&n,&V);
for(int i=1,v,w,k;i<=n;i++){
scanf("%d%d%d",&v,&w,&k);
for(int mod=0;mod<w;mod++){
ff=tt=1,a[1]=0,val[1]=f[mod];
for(int num=1;num*w+mod<=V;num++){
while(ff<=tt&&num-a[ff]>k) ++ff;
while(ff<=tt&&f[num*w+mod]-num*v>=val[tt]) --tt;
a[++tt]=num,val[tt]=f[num*w+mod]-num*v;
//要是 num 把 a[ff] 顶替掉了就说明不取当前物品最优。
//这里只能用 val,因为 f 已经被覆盖了。
//val[ff]+v*a[ff]+v*(num-a[ff])=val[ff]+v*num
f[mod+num*w]=val[ff]+v*num;
}
}
}
printf("%d\n",f[V]);
return 0;
}

浙公网安备 33010602011771号