多重背包问题
有 \(N\) 种物品和一个容量是 \(V\) 的背包。
第 \(i\) 种物品最多有 \(s_i\) 件,每件体积是 \(v_i\),价值是 \(w_i\)。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,\(N,V\),用空格隔开,分别表示物品种数和背包容积。
接下来有 \(N\) 行,每行三个整数 \(v_i, w_i, s_i\),用空格隔开,分别表示第 \(i\) 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
\(0 \lt N \le 1000\)
\(0 \lt V \le 2000\)
\(0 \lt v_i, w_i, s_i \le 2000\)
提示:
本题考查多重背包的二进制优化方法。
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
代码实现
朴素版
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
int n, m;
int v[N], w[N], s[N];
int f[N][N];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i] >> s[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);
cout << f[n][m] << endl;
return 0;
}
//https://www.acwing.com/activity/content/code/content/57826/
//来源:AcWing
二进制优化版
#include <iostream>
#include <algorithm>
using namespace std;
/*二进制优化(下方举例说明):
①假定物品件数s=200
②二进制形式k[n]={1,2,4,8,16,32,64,73} 其所有值相加是<=200 可以凑出[0~200]的所有数
③除开最后一个数不是二进制形式,73=200-(1+2+4+8+16+31+64)
④其余数,若定64,其中64+[0~63]->[0~127]
⑤最后一个数73,其中73+[0~127]->[0~200] */
const int N=12010,M=2010; //此处1000 * log2000 , 向上取整数N 得12000
int n,m; //n表示物品总数,m表示背包容积
int v[N],w[N]; //v[i]表示第i件物品的体积,w[i]表示第i件物品的价值
int f[N]; //组合方案中的最大价值
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; //此处二进制优化,从1开始分(假定选择当前物品的总数为k)
while(k<=s)
{
cnt++; //编号增加
v[cnt]=a*k; //体积*物品总数
w[cnt]=b*k;
s-=k; //从总个数s中减去k个
k*=2; //二进制,乘以二继续循环
}
if(s>0) //若总个数s还有剩余
{
cnt++; //编号增加
v[cnt]=a*s; //体积*剩余个数
w[cnt]=b*s;
}
}
n=cnt; //将n更新为cnt
for(int i=1;i<=n;i++)
for(int j=m;j>=v[i];j--) //此处优化成一维,次数使用倒序,因为原f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+w[i]*k)
f[j]=max(f[j],f[j-v[i]]+w[i]); //其中v[i]和w[i]已经是选定个数k后更新的体积和价值
cout<<f[m]<<endl;
return 0;
}
//https://www.acwing.com/activity/content/code/content/57846/
//来源:AcWing

浙公网安备 33010602011771号