题目
给定两个长度都为N的数组weights和values,weights[i]和values[i]分别代表 i号物品的重量和价值
给定一个正数bag,表示一个载重bag的袋子,装的物品不能超过这个重量
返回能装下的最大价值
暴力递归
首先建立尝试函数process,参数为w数组、v数组、当前进行到的序号index、背包剩余的容量rest,返回值为包中物品的总价值
那么主函数中调用的就是process(w,v,0,bag)
首先分析basecase:
当index==N时,所有物品都以选完,返回0
当rest<0时,背包已经无法容纳物品,返回0
接下来就是选择问题,
- 要index号货物,返回该号货物的价值再加上做完这个选择以后之后要执行的递归操作process(w,v,index+1,bag-w[index])
- 不选index号货物,返回之后的递归操作 process(w,v,index+1,bag)
但是此时的basecase存在问题:因为当rest小于0的时候,上一次的放入操作就是违反规则的,所以应该对返回值做一个判断,如果等于-1,那么就把上一次加入到背包中的物品取出来。
那么应该如何写,当要index货物的时候,先用一个变量next接收之后递归的返回值,如果返回的是-1,那么说明index号的货物不能要,此时只有这一个选项。如果返回值不是-1,则还是原来默认选择当前货物的值。
最终返回两种选择之前较大的值,就是结果。
int process(vector<int>& weight, vector<int>& value, int index, int rest) {
if (rest < 0) {
return -1;
}
if (index == weight.size()) {
return 0;
}
int p1 = process(weight, value, index + 1, rest);
int p2 = 0;
int next = process(weight, value, index + 1, rest - weight[index]);
if (next != -1) {
p2 = value[index] + next;
}
return max(p1, p2);
}
int way1(vector<int>& weight, vector<int>& value,int bag) {
return process(weight, value, 0, bag);
}
注:basecase当中要先返回rest为-1 的情况,这种情况是错误的,而货物选完返回0是正常的basecase,两者的顺序反了,代码也会出错。
动态规划
尝试函数有两个可变参数,index当前货物编号,活动范围是0到N 一共N+1个;rest是当前背包剩余容量,范围是负数到rest 。所以建立一个dp表,大小为dp[N+1][bag+1]
要求的位置是dp[0][bag]
根据basecase可得,当index为N的时候,即所有货物都已经选完了,最后一行都为0.
再观察普通位置依赖可得,上一行的值依赖下一行。
所以直接改动态规划
int way2(vector<int>& weight, vector<int>& value, int bag) {
int N = weight.size();
vector<vector<int>>dp(N + 1, vector<int>(bag + 1, 0));
for (int index = N - 1; index >= 0; index--) {
for (int rest = 0; rest <= bag; rest++) {
int p1 = dp[index + 1][rest];
int p2 = 0;
int next = (rest - weight[index]) < 0 ? -1 : dp[index + 1][rest - weight[index]];
if (next != -1) {
p2 = value[index] + next;
}
dp[index][rest] = max(p1, p2);
}
}
return dp[0][bag];
}