参考【真的很详细】:多重背包问题大全(超详细)_曼切斯特的流氓的博客
普通的多重背包
三重循环复杂度;
【注意:这是把多重背包拆分成 01背包 所以在用一维dp数组的时候,体积记得要倒着来!!!】
【注意这里是外层枚举体积,内层枚举物品个数】
#include<bits/stdc++.h>
using namespace std;
int dp[120];
int s,v,w;
int main()
{
int m,n;cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>v>>w>>s;
for(int j=m;j>0;j--){
for(int k=1;k<=s&&j-v*k>=0;k++){
dp[j]=max(dp[j],dp[j-v*k]+w*k);
}
}
}
cout<<dp[m];
return 0;
}
二进制优化多重背包
通过二进制来代表拿的物品数,用二进制将同种物品 分别 合并;
【注意:外层枚举新合成出来的物品,内层枚举体积】【注意体积要倒着来,就像01背包一样】
#include <iostream>
using namespace std;
int n,m,v[15],w[15],dp[2010];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int cnt=0;int tiji,jia,s;
cin>>tiji>>jia>>s;
for(int k=1; k<=s; k<<=1){
v[++cnt]=k*tiji;w[cnt]=k*jia;
s-=k;
}
if(s){v[++cnt]=s*tiji;w[cnt]=s*jia;}
for(int k=1;k<=cnt;k++){
for(int j=m;j-v[k]>=0;j--){
dp[j]=max(dp[j],dp[j-v[k]]+w[k]);
}
}
}
cout<<dp[m];return 0;
}
单调队列优化
用数组来充当双端队列,stl会TLE;
当前为v,w,s分别为体积价值件数,
Q1:当前的体积是k,那么这个dp[k]是由什么更新的?(这里的k不等同于代码里的k)
——是由dp[k-s*v]……dp[k-2*v]、dp[k-v]、dp[k]一步步推来;
Q2:那么有多少种dp[k-s*v]……dp[k-2*v]、dp[k-v]、dp[k]序列呢?
——有0,1,…… v-2,v-1,共 v 种
(因为0和v是在同一个序列中,1和v+1在同一个序列中……)
1. 代码中的 j 是序列的种数,范围是【0,v-1】;(同一个i里,不同种的序列不会相互影响)
2. 第 j 种序列里的 第k个体积量,若双端队列里的头是小于 k-s*v ,则它对k就没有影响了,出队;
3. 维护单调队列:
q[tail] 是第j种序列中的一员,q[tail]的“潜力”是 g[q[tail]](上一个状态)再加上能塞下的物品数量(k-q[tail])/v 乘以每件的单价w;
g[k]和g[q[tail]]+(k-q[tail])/v*w比大小,若g[k]大,q[tail]]弹出,于是队列里就是从队头到队尾,“潜力”从大到小的排序;
4. 动态转移方程:
因为本来是要像01背包一样倒着来的,但是单调队列没法倒着(指我不会),所以这里要么 二维数组 要么 两个一维数组(当前状态和上一个状态),这里g是上一个状态,dp是当前状态;
当前量dp[k]的值要么是 上一个状态g[k]的值 ,要么是 单调队列中的队首的“潜力”,在这里两个值中选最大值;
【注意⚠】
1. memcpy函数的用法 memcpy_百度百科 (baidu.com)
2. 下面的代码head和tail是指向变量本身而非下一个位置,所以注意入队的时候是++tail;
3.注意单调队列维护的时候有2个条件:队列不为空 和 g[k]大于队尾的“潜力”;
#include<bits/stdc++.h>
using namespace std;
int dp[20005],g[20005],q[20005];
int main()
{
int n,m;cin>>n>>m;
for(int i=0;i<n;i++){
int v,w,s;cin>>v>>w>>s;
memcpy(g,dp,sizeof(g));
for(int j=0;j<v;j++){
int head=0,tail=-1;//初始化注意
for(int k=j;k<=m;k+=v){
if(head<=tail){if(q[head]<k-s*v)head++;}//队头弹出
while(head<=tail&&g[k]>=g[q[tail]]+(k-q[tail])/v*w){tail--;}//队尾弹出
q[++tail]=k;//入队
dp[k]=max(g[k],g[q[head]]+(k-q[head])/v*w);//状态转移方程
}
}
}
cout<<dp[m];
return 0;
}
单调队列用stl的话会TLE,甚至比二进制优化还要慢,清空的时候哪怕是新建队列也会T,如果有人知道为什么的话求评论解答;
会被TLE的代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;const int maxn=20010;int f[maxn],g[maxn];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
memcpy(g,f,sizeof(f));
int v,w,s;scanf("%d%d%d",&v,&w,&s);
for(int j=0;j<v;j++){
deque<int>q;
for(int k=j;k<=m;k+=v){
if(!q.empty()&&k-s*v>q.front()) q.pop_front();
if(!q.empty()) f[k]=max(g[k],g[q.front()]+(k-q.front())/v*w);
while(!q.empty()&&g[k]>=g[q.back()]+(k-q.back())/v*w) q.pop_back();
q.push_back(k);
}
}
}
printf("%d\n",f[m]);
return 0;
}
混合背包例题(做个小测试) 7. 混合背包问题 - AcWing题库
代码 :
#include<bits/stdc++.h>
using namespace std;
int vv[20],ww[20];
int dp[1005];
int main()
{
int n,m;cin>>n>>m;
for(int i=0;i<n;i++){
int v,w,s;cin>>v>>w>>s;
if(s!=-1&&s!=0&&s!=1){
int cnt=0,k;
for(k=1;k<=s;k<<=1){
vv[++cnt]=k*v;ww[cnt]=k*w;
s-=k;
}
if(s)vv[++cnt]=s*v;ww[cnt]=s*w;
for(int k=1;k<=cnt;k++){
for(int j=m;j-vv[k]>=0;j--){
dp[j]=max(dp[j],dp[j-vv[k]]+ww[k]);
}
}
}
else if(s==-1||s==1)
for(int j=m;j>=v;j--)dp[j]=max(dp[j],dp[j-v]+w);
else if(s==0)
for(int j=v;j<=m;j++)dp[j]=max(dp[j],dp[j-v]+w);
}
cout<<dp[m];
return 0;
}