多重背包
多重背包
题目描述
有
第
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,
接下来有
输出格式
输出一个整数,表示最大价值。
数据范围
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例
10
代码
多重背包朴素
状态表示 :
属性: MAX
状态计算:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-k*c[i]]+k*w[i])
dp[i-1][j]:不选第 i 种物品,直接由上一层转移
dp[i-1][j-k*c[i]]+k*w[i]:选第 i 种物品k件,由上一层空出体积k*c[i]的状态+k*w[i]得来
与完全背包状态表示以及转移相同
#include<iostream>
#include<cstdio>
using namespace std;
int n,v,c[1500],w[1500],s[1500],dp[1500][1500];
int main(){
scanf("%d%d",&n,&v);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&c[i],&w[i],&s[i]);
for(int i=1;i<=n;i++) // 枚举物品
for(int j=0;j<=v;j++) // 枚举体积
for(int k=0;k<=s[i]&&k*c[i]<=j;k++) // 枚举决策
dp[i][j]=max(dp[i][j],dp[i-1][j-k*c[i]]+k*w[i]);
printf("%d",dp[n][v]);
}
多重背包二进制优化
由于每组物品的件数均不一样,所以不能使用完全背包的优化方法(具体件数不可控),因此采用另一种思路——二进制优化。
将每一种物品由1.2.4.8.16.128...的件数打包,不足一组的零头重新打包,转化为01背包问题
//多重背包二进制拆分
#include<iostream>
#include<cstdio>
using namespace std;
int n,v,c[12500],w[12500],dp[25000];
int cnt;//划分成01背包后的总包数
int main(){
scanf("%d%d",&n,&v);
while(n--){
int a,b,s;
scanf("%d%d%d",&a,&b,&s);
//倍增思想
int k=1; //相当于base(每组件数):1 2 4 8 16 32 64 128 256...据此打包
while(k<=s){
cnt++;
c[cnt]=k*a;
w[cnt]=k*b;
s-=k;
k*=2;
}
if(s>0){ //若拆完之后还有零头
cnt++; //再分一个包
c[cnt]=a*s;
w[cnt]=b*s;
}
}
//相当于将多重背包转化为01背包
n=cnt;//01物品总个数
for(int i=1;i<=n;i++)
for(int j=v;j>=c[i];j--)//注意倒序遍历
dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
printf("%d",dp[v]);
}
多重背包单调队列优化
其实一般很少会被逼到需要使用单调队列优化的背包了
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 20010;
int n, m;
int f[N], g[N], q[N];
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i ++ )
{
int v, w, s;
cin >> v >> w >> s;
memcpy(g, f, sizeof f);
for (int j = 0; j < v; j ++ )
{
int hh = 0, tt = -1;
for (int k = j; k <= m; k += v)
{
if (hh <= tt && q[hh] < k - s * v) hh ++ ;
while (hh <= tt && g[q[tt]] - (q[tt] - j) / v * w <= g[k] - (k - j) / v * w) tt -- ;
q[ ++ tt] = k;
f[k] = g[q[hh]] + (k - q[hh]) / v * w;
}
}
}
cout << f[m] << endl;
return 0;
}