题目(卡玛网T46):
小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的空间,并且具有不同的价值。
小明的行李空间为 N,问小明应该如何抉择,才能携带最大价值的研究材料,每种研究材料只能选择一次,并且只有选与不选两种选择,不能进行切割。
方法:本体和昨天的题目一样,只是昨天在解决问题时我们使用了二维数组,一个代表物品一个代表背包。但其实我们通过递推公式dp[i][j]=max(dp[i-1][j], dp[i][j-weight[i]] + value[j])可以发现我们可以简化问题。原因在于我们在计算dp[i][j]时使用的是从上一层推导而来的i-1,而我们如果在计算dp[i][j]时将上一层的值拷贝下来,在dp[j]进行计算,就可以将问题转化为一维数组的问题。同时递推公式可以变为dp[j] = max(dp[j], dp[j - weight[i]] + value[i]),也即去掉了一个维度。同时这种情况下我们需要考虑更多的问题
1:dp数组含义:dp[j]代表任取0-i的物品,容量为j的背包所能获得的最大价值
2:地推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
3:初始化:因为递推公式中有max函数,因此不能影响计算出来的值,要用最大的非负整数0
4:遍历顺序:一维数组的遍历顺序不像二维数组那么随意,只能是先遍历物品再遍历背包,并且遍历背包时只能从最大容量的背包开始往小遍历,原因是因为我们使用了一维数组,如果正序遍历背包会出现重复放置一个物品的情况,这和实际不相符。从另一个角度来看,因为我们将数组压缩,本层数组拷贝了上一层的数值,如果从前往后遍历,每次使用的数值是刚计算出来的本层的值而不是上一层拷贝下来的值。
5:举例推导:略
方法:
#include<bits/stdc++.h>
using namespace std;
int main(){
int m, n;
cin >> m >>n;
vector<int> costs(m);
vector<int> value(m);
for(int i = 0; i < m; i++){
cin >> costs[i];
}
for(int i = 0; i < m; i++){
cin >> value[i];
}
vector<int> dp(n + 1, 0);
for(int i = 0; i < m; i++){
for(int j = n; j >= costs[i]; j--){
dp[j] = max(dp[j], dp[j - costs[i]] + value[i]); //递推公式
}
}
cout << dp[n] << endl;
return 0;
}