11083 旅游背包(优先做)
时间限制:1000MS 代码长度限制:10KB
提交次数:0 通过次数:0
题型: 编程题 语言: G++;GCC;VC;JAVA
Description
想去旅游吗?那得先准备背包!
背包用来装旅游物品,现在共n种(n<=50)旅游物品,每种物品都有体积vi,重量wi,数量ci,价值ti
(vi,wi,ci和ti都为整数)。
限制体积最多V立方厘米(V<=1000),重量最多W公斤(W<=500)。
请问你如何选择物品,使得带上的物品总价值最大,这个最大总价值为多少?
比如:
物品 体积 重量 数量 价值
编号 (立方厘米) (公斤) (个) (元)
1 30 3 10 4
2 50 8 10 5
3 10 2 10 2
4 23 5 8 3
5 130 20 5 11
若V为500,W为100,则选择物品的最大价值为72(且72=104+102+4*3:由10件物品1,10件物品3,
和4件物品4组成)。
这是一个多维且有界的背包问题,属于常规0-1背包问题的扩展问题。
输入格式
第一行,物品的种类n,背包体积的限制V,背包载重量的限制W。n,V和W的范围如前所述。
接下来n行,每行为该种物品i的体积vi,重量wi,数量ci,价值ti (规定vi,wi,ci和ti都为整数)。
输出格式
仅一行,为选择物品子集所能获得的最大价值。
输入样例
5 500 100
30 3 10 4
50 8 10 5
10 2 10 2
23 5 8 3
130 20 5 11
输出样例
72
解题思路
思路同01背包,只是稍稍拓展了一下思路,没提升多少难度
01背包题如下:算法设计与分析 SCAU19185 01背包问题 C++
1. dp 方程定义
- dp[i][j][k] 表示从前 i 个物品中选,选出的总体积小于等于 j,总重量小于等于 k 的最优解。
- 可以从 dp[1][1][1] = 0 开始遍历,有 n 个物品,需要 n 次遍历
- 而在每件物品时都有 V 个容纳体积、W 个容纳重量可支配,再进行 V 和 W 次遍历(顺序可颠倒)。
- 因此 dp[i][j][k] 不断由之前的状态更新而来。
2. 状态转移方程
我们将所有状态分为两类,第一类不含第 i 件物品,第二类含第 i 件物品,取决于第 i 件物品体积是否大于当前背包所能容纳的大小以及物品重量是否大于当前背包所能容纳的大小。例如:当前背包体积最大容纳量只有5,如果物品体积为7,那么很明显装不进来,同理如果背包最大载重为10,如果第 i 件物品重量为 20,很明显装不进来;
即注意第二类只有在 v[i] <= j 和 w[i] <= k 时才存在。
v[i] 为第 i 件物品体积,w[i] 为第 i 件物品重量,背包当前体积容量为 j,重量容量为 k
- 第一类不含 i,那就是在前 1 到 i - 1 件物品中选容量不超过 j 的最优选,最优选为 dp[i - 1][j][k]。
- 第二类为含 i 的,因为直接求解不好求,我们可以先将 i 减去,最后在加上 i。也就是在前 1 到 i - 1 中选物品体积不超过 j - v[i] 、重量不超过 k - w[i] (j 为背包当前体积最大容纳量,v[i] 为当前要添加的物品体积);此时价值为 dp[i - 1][j - v[i]][k - w[i]] + val[i]; 例如:当前容纳体积为 j = 5,重量为 k = 6,如果要加入体积为 3,重量为 5 的物品,那么肯定是在计算体积为 2 ,重量为 1 的价值基础上,再加上新的物品价值。
- 最后我们要做的时求第一类和第二类的 max;
3. 算法解题思路
很多小伙伴注意到,此题还有一个条件,那就是各种物品有数量,那么我们如何处理呢?
其实很简单,我们把其一个个处理就行,意思是比如 10 个物品甲,那么我们在录入物品数组 v[i]、 w[i]、 val[i] 时,录入十次该物品即可。
- 循环录入数据时,用双层,外层是物品种类,内层是该类物品数量(因为我们目的是把物品的重量、体积、价值录成数组,肯定是思考如何将全部数据录入)。
- 三层 for 循环,外层是第 i 件物品,内层是背包所能容纳的最大体积 j,最内层是背包所能容纳的最大重量 k
- 判断,若此时要加入的第 i 件物品体积如果直接大于背包当前所能容纳的体积 j 或 质量直接大于,那么加入不了,直接取上一个物品相同容纳体积的数据
- 若能装,需进行决策是否选择第 i 个物品,第二个比较对象为:在减去要新加的物品体积 v[i] 基础上,加上新价值看看如何,即 dp[i][j] = max(dp[i - 1][j][k], dp[i - 1][j - v[i]][k - w[i]] + val[i]);
更多注释可查看下方的完整代码中,有助于理解
代码如下
#include <iostream>
using namespace std;
int dp[501][1001][501] = { 0 }; // 由于这题跟 01背包 相比,不仅有体积,还有重量,所以需要三维DP
/*
5 500 100
30 3 10 4
50 8 10 5
10 2 10 2
23 5 8 3
130 20 5 11
*/
int main()
{
int n, V, W;
cin >> n >> V >> W;
int i, j, k;
int vi, wi, ci, ti;
int v[1000] = { 0 }; //体积 10个物体1就有10个v,10个w,10个Val
int w[500] = { 0 }; //重量
int val[500] = { 0 };//价值
// 对于此题的数量,解题思路可以是将其拆解成多个输入,即数量是10时,记录10个该物品的体积,重量,价值进去
// p 的作用是一个指针,内容是记录当前记录到第几个物品的信息了
int p = 0;
for(int i = 1; i <= n; i++){
cin >> vi >> wi >> ci >> ti;
for(int i = 1; i <= ci; i++){
p++;
v[p] = vi;
w[p] = wi;
val[p] = ti;
}
}
// 装前 i 件物品,体积容量为 j,重量容量为 k 时的最大价值
for(i = 1; i <= p; i++) {
for(j = 1; j <= V; j++) {
for(k = 1; k <= W; k++) {
// 此时要加入的第i件物品体积如果直接大于 背包当前所能容纳的体积j,或重量大于,那么加入不了,直接取上一个物品相同容纳体积的数据
if(v[i] > j || w[i] > k) {
dp[i][j][k] = dp[i - 1][j][k];
} else {
// 能装,需进行决策是否选择第i个物品,第二个比较对象为:在减去要新加的物品体积v[i] 基础上,加上新价值看看如何
// 例如:当前容纳体积为 j=5,如果要加入体积为3的物品,那么肯定是在计算体积为2的价值基础上,再加上新的物品价值
dp[i][j][k] = max(dp[i - 1][j][k], dp[i - 1][j - v[i]][k - w[i]] + val[i]);
}
//cout << "i=" << i << " j=" << j << " " << " k=" << k << " " << dp[i][j][k] << endl;
}
}
}
cout << dp[p][V][W] << endl;
return 0;
}
最后
对我感兴趣的小伙伴可查看以下链接
- 我的掘金主页:https://juejin.cn/user/1302297507801358
- 博客主页:http://blog.zhangjiancong.top/
- 公众号:Smooth前端成长记录