前情提要:动态规划——背包九讲——多重背包问题
多重背包问题
有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。
这题目和完全背包问题很类似,特点是:每种物品都有自己特异的件数、花费、价值。
朴素算法:
每种物品有n[i]件可以取用,需要枚举每种物品选了多少件(枚举件数不能超过背包容量)
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
for(int k=0;k<=s[i]&&k*v[i]<=j;k++) //与完全背包相比仅仅是加了一个判断条件而已
f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+w[i]*k);
朴素算法的时间效率很低,通过下面的二进制优化可以把遍历每种物品的n[i]件物品的时间复杂度从O(n)优化为O(log n)
优化:通过展开化简式子不可行,换用二进制优化方式
正篇:背包问题的经典二进制优化
第i种物品是有n[i]件的,这个n[i]可以被展开为n[i]=20+21+…+2k+c (其中2k+1 < n[i] < 2k+2)
(20+21+…+2k可以表示区间[ 0,2k+1-1 ]的数,通过加上c这个数,能够表示区间[0,n[i]]上的所有数)
同时记录这些加数的值
那这样的话对于[0,n[i]]的每一个数,都可以用这些二进制数表示。即为将n种物品转化成cnt种物品【每种物品的可选择数量都是2的某次方(某种物品的最后一种例外)】
完整代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 24010, M = 2010; //数组开的元素个数N --> 2000*log2000 (log2000约等于11.几)
int n, m;
int v[N], w[N];
int f[M];
int main()
{
cin >> n >> m;
int cnt = 0;
for (int i = 1; i <= n; i ++ )
{
int a, b, s;
cin >> a >> b >> s;
int k = 1;
while (k <= s)
{
cnt ++ ;
v[cnt] = a * k;
w[cnt] = b * k;
s -= k;
k *= 2;
}
if (s > 0) //加上最后一位c
{
cnt ++ ;
v[cnt] = a * s;
w[cnt] = b * s;
}
}
n = cnt; //将n种物品转化成cnt种物品【每种物品的可选择数量都是2的某次方(某种物品的最后一种例外)】
for (int i = 1; i <= n; i ++ ) //此处包含0 1背包问题的优化,可详见我的背包专题
for (int j = m; j >= v[i]; j -- )
f[j] = max(f[j], f[j - v[i]] + w[i]);
cout << f[m] << endl;
return 0;
}
来源:CSDN
作者:TSD_captain
链接:https://blog.csdn.net/qq_33164724/article/details/104543083