完全背包(从二维数组到一维滚动数组)

完全背包 :有一个背包的容积为V,有N个物品,每个物品的体积为v[i],权重为w[i],每个物品可以取无限次放入背包中,背包所有物品权重和最大是多少?

背包最大重量为4,每个物品有无限个,物品的重量和价值如下:

重量价值
物品0115
物品1320
物品2430

二维dp数组完全背包

1. 确定dp数组以及下标的含义

dp[i][j]数组表示从前i个物品中任意取,放进容量为j的背包中所得到的物品的最大价值。

2. 确定递推公式

有两个方向推出来dp[i][j]:

  • 不放物品i:由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是dp[i - 1][j]

    (其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以背包内的价值依然和前面相同。)

  • 放物品i:由dp[i][j - weight[i]]推出,注意这里和01背包的区别,因为可以重复放物品i,所以是dp[i][j - weight[i]],表示背包容量为j - weight[i]的时候任取物品i的最大价值

  • 递归公式 dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i]);

    • 如果物品i的重量大于背包容量j时,背包放不下,就不能选择物品i,dp[i][j] = dp[i - 1][j]
    • 如果物品i的重量小于等于背包容量j时,dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])

3. dp数组如何初始化

关于初始化,一定要和dp数组的定义一致。

两种初始化情况:

  • 背包容量j为0,不管选择哪些物品,所得到的物品最大价值一定为0,所以dp[i][0] = 0
  • 物品数量为0时,不论背包容量多大,所得到的物品最大价值一定为0,所以dp[0][j] = 0
dp[i][j](物品\背包)01234
无物品00000
物品00
物品10
物品20

4. 确定遍历顺序

有两个遍历的维度:物品和背包,那么问题来了,先遍历物品还是先遍历背包重量呢?

其实都可以,因为递推公式为dp[i][j] = dp[i - 1][j]dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])这两种情况,不论哪种,dp[i - 1][j]dp[i][j - weight[i]]都在dp[i][j]的左上角方向,而两种遍历顺序都能够获取到dp[i][j]左上角方向的值,先遍历物品会更好理解

5. 举例推导dp数组

dp[i][j](物品\背包)01234
无物品00000
物品0(重量1,价值15)015304560
物品1(重量3,价值20)015304560
物品2(重量4,价值30)015304560

完整代码:

/*********************************************************************
完全背包
有n件物品和一个最多能背重量为w的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。
每件物品有无限个,即一个物品可以放入背包多次,求解将哪些物品装入背包里物品价值总和最大。
重量 价值
物品0	1	15
物品1	3	20
物品2	4	30
*********************************************************************/
#include <iostream>
#include <vector> 
#include <string>
#include <algorithm> 
using namespace std;

// 先遍历物品,在遍历背包
int bag1(vector<int> weight, vector<int> value, int bagweight) {
	vector<vector<int>> dp(weight.size() + 1, vector<int>(bagweight + 1, 0));

	for (int i = 1; i <= weight.size(); i++){
		for (int j = 0; j <= bagweight; j++){	// 遍历背包
			if (j < weight[i - 1]){
				dp[i][j] = dp[i - 1][j];
			}
			else{
				// 注意这里跟01背包只有下面一个下标不同,那就是“放i”这个选择,因为是可以重复放的,所以是dp[i]
				dp[i][j] = max(dp[i - 1][j - 1], dp[i][j - weight[i - 1]] + value[i - 1]);
			}
		}
	}
	for (auto x : dp) {
		for (auto xx : x) {
			cout << xx << ' ';
		}
		cout << endl;
	}
	return dp[weight.size()][bagweight];
}

int main(){
	vector<int> weight = { 1, 2, 3, 4 };
	vector<int> value = { 10, 21, 30, 43 };
	int bagweight = 5;
	int ret = bag1(weight, value, bagweight);
	cout << ret << endl;
	system("PAUSE");
	return 0;
}

一维滚动dp数组完全背包

1.确定dp数组以及下标的含义

dp[j]表示容量为j的背包中所得到的物品的最大价值。

2. 确定递推公式

递推公式变为dp[j] = max(dp[j], dp[j - weight[i]] + value[i])

3. dp数组如何初始化

dp[0] = 0即可

4. 确定遍历顺序

01背包内嵌的循环是从大到小遍历,为了保证每个物品仅被添加一次;而完全背包的物品是可以添加多次的,所以要从小到大去遍历

5. 举例推导dp数组

完整代码:

/*********************************************************************
完全背包
有n件物品和一个最多能背重量为w的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。
每件物品有无限个,即一个物品可以放入背包多次,求解将哪些物品装入背包里物品价值总和最大。
重量 价值
物品0	1	15
物品1	3	20
物品2	4	30
*********************************************************************/
#include <iostream>
#include <vector> 
#include <string>
#include <algorithm> 
using namespace std;

// 先遍历物品,在遍历背包
int bag1(vector<int> weight, vector<int> value, int bagweight) {
	vector<int> dp(bagweight + 1, 0);

	// 先遍历物品,再遍历背包
	for (int i = 0; i < weight.size(); i++) { // 遍历物品
		for (int j = weight[i]; j <= bagweight; j++) { // 遍历背包容量
			dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

		}
	}
	return dp[bagweight];
}

int main(){
	vector<int> weight = { 1, 2, 3, 4 };
	vector<int> value = { 10, 21, 30, 43 };
	int bagweight = 5;
	int ret = bag1(weight, value, bagweight);
	cout << ret << endl;
	system("PAUSE");
	return 0;
}
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值