2332: 【入门】环游世界之背包问题
内存限制:128 MB
时间限制:1.000 S
评测方式:文本比较
命题人:
提交:2
解决:2
题目描述
张老师准备环游世界,出发之前要做的最重要的事情,当然是整理自己的背包啦。张老师有一个容积为m的背包 ,有n个物品作为放入背包的待选物品 ,每样东西都有自己的价值Wi,和体积Vi ,第i个物品有Ni个 (Ni=0时表示有无限多个),请你编程帮助张老师计算,他的背包能够存入的最大价值是多少?
30%数据满足 1 <= m,n <=1000
100%数据满足 1 <=m,n <= 10000
输入
第1行有2个整数N,M,表示物品的种类和背包的容积;
第2-N+1行,每行有3个整数Vi,Wi,Pi,分别表示每个物品的体积,价值,个数。(体积<=5000,价值<=5000,个数<=1000)
第2-N+1行,每行有3个整数Vi,Wi,Pi,分别表示每个物品的体积,价值,个数。(体积<=5000,价值<=5000,个数<=1000)
输出
一个整数,表示能够存入背包的最大价值。
样例输入 复制
5 50
1 1 50
2 4 3
48 49 1
1 51 1
3 3 3
样例输出 复制
106
提示
题目分析
这是多重背包问题: n 种物品,背包容量 m;每种物品 i:体积\(V_i\),价值\(W_i\),数量\(P_i\); \(P_i=0\)代表无限个(完全背包)。 数据范围:\(n,m\le 10000\),物品数量\(P_i\le1000\)。
优化方案:二进制拆分,把多重背包拆成 01 背包,\(P_i=0\)单独按完全背包处理。
状态:\(dp[j]\) 表示背包体积 j 能装的最大价值。 一维数组,逆序遍历(01 背包),顺序遍历(完全背包)。
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXM = 10005;
int dp[MAXM];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
int v, w, p;
cin >> v >> w >> p;
if(p == 0)
{
// 完全背包:顺序
for(int j = v; j <= m; j++)
{
dp[j] = max(dp[j], dp[j - v] + w);
}
}
else
{
// 二进制拆分
int k = 1;
int cnt = p;
while(cnt > 0)
{
int take = min(k, cnt);
int tv = take * v;
int tw = take * w;
// 01背包逆序
for(int j = m; j >= tv; j--)
{
dp[j] = max(dp[j], dp[j - tv] + tw);
}
cnt -= take;
k *= 2;
}
}
}
cout << dp[m] << endl;
return 0;
}