这个是我最近最引以为傲的理解,(大佬勿喷),咱们话不多说直接上题
模板题来自acwing多重背包问题Ⅲ
圣人惠额滴神啊!!!!
题目:有N种物品和一个容量是V 的背包。第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi,求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。输出最大价值。
这里dp的思路:q[l]是小于等于l体积所能装下的价值最大值,k个物品,j遍历的是V/vi的余数,思路的核心,就是利用q[l]来储存体积从j=0到余数的价值最大值直接进行相加,省略了类似01背包组合物品的思想,直接维护以一个体积能装下的价值最大值。再用这个储存的最大值来进行递推。
递推式如下:
dp[k*vi+j]=max(dp[k*vi+j],q[l]+k*wi);
了解了思路就要想办法实现了,怎么才能完成一个单调队列获得一段数列中的最大值呢 ?
int temp=dp[k*vi+j]-k*wi;
while(l<r&&q[r-1]<=temp)r--;
q[r]=temp;
num[r++]=k;
while(l<r&&k-num[l]>si)l++;
这里给出代码,明天给出单调队列的思路 (孩子累了。。。。明天写完加上传送门)
给出ac代码
#include<bits/stdc++.h>
using namespace std;
int dp[200005],q[500000],num[100005];
int l,r;
int main(){
int n,v;
cin>>n>>v;
int vi,wi,si;
for(int i=1;i<=n;i++){
cin>>vi>>wi>>si;
if(si>v/vi)si=v/vi;
for(int j=0;j<vi;j++){
l=r=1;
for(int k=0;k<=(v-j)/vi;k++){
int temp=dp[k*vi+j]-k*wi;
while(l<r&&q[r-1]<=temp)r--;
q[r]=temp;
num[r++]=k;
while(l<r&&k-num[l]>si)l++;
dp[k*vi+j]=max(dp[k*vi+j],q[l]+k*wi);
}
}
}
cout<<dp[v];
}
给个小问题:输出的为什么是dp[V],为什么不是在dp数组中遍历寻找最大值?
其实想法和上期讲01背包的初始化相似,当维护q[l]时,0体积0价值的物品仍是合法存在的。
梳理几种背包优化方式和困惑(第一期)_我们教练不会签到的博客-CSDN博客