赞
踩
对于背包问题,有一种写法, 是使用二维数组,即dp[i][j] 表示为从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
有两个方向推出来dp[i][j],
不放物品i:由dp[i - 1][j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以背包内的价值依然和前面相同。)
放物品i:由dp[i - 1][j - weight[i]]推出,dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i - 1][j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值
所以递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
由图可知dp[i - 1][j]在dp[i][j]的上方, dp[i - 1][j - weight[i]]在dp[i][j]的左上方,所以只需要确定前面的值即可推出dp[i][j]。
假设题目要求为:
首先从dp[i][j]的定义出发,如果背包容量j为0的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为0。如图:
状态转移方程 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。
dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。
那么很明显当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。
当j >= weight[0]时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。
此时dp数组初始化情况如图所示:
dp[0][j] 和 dp[i][0] 都已经初始化了,那么其他下标应该初始化多少呢?
其实从递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]); 可以看出dp[i][j] 是由左上方数值推导出来了,那么 其他下标初始为什么数值都可以,因为都会被覆盖。
初始-1,初始-2,初始100,都可以!
但只不过一开始就统一把dp数组统一初始为0,更方便一些。
如图:
- // dp数组, dp[i][j]代表行李箱空间为j的情况下,从下标为[0, i]的物品里面任意取,能达到的最大价值
- //定义dp[i][j]二维数组 初始化i长度物品数量 j长度为背包容量加1
- vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0));
-
- // j < weight[0]已在上方被初始化为0
- // j >= weight[0]的值就初始化为value[0]
- for (int j = weight[0]; j <= bagweight; j++) {
- dp[0][j] = value[0];
- }
- //二维dp数组实现
- #include <bits/stdc++.h>
- using namespace std;
-
- void solve(int n,int bagweight) {
-
- vector<int> weight(n, 0); // 存储每件物品所占空间
- vector<int> value(n, 0); // 存储每件物品价值
- vector<int> isLoad(n, 0);
-
- //初始物品重量和物品价值
- for(int i = 0; i < n; ++i) {
-
- cout<<"请分别输入第"<<i+1<<"个物品的重量和价值:"<<endl;
- cin >> weight[i];
- cin >> value[i];
- }
-
-
- // dp数组, dp[i][j]代表行李箱空间为j的情况下,从下标为[0, i]的物品里面任意取,能达到的最大价值
- //定义dp[i][j]二维数组 初始化i长度物品数量 j长度为背包容量加1
- vector<vector<int>> dp(weight.size(), vector<int>(bagweight + 1, 0));
- //dp[i][j] = max{dp[i-1][j],dp[i-1][j-weight[i]]+value[i]} = 最大值{不装i物品,装i物品}
-
-
- // 初始化, 因为需要用到dp[i - 1]的值
- // j < weight[0]已在上方被初始化为0
- // j >= weight[0]的值就初始化为value[0]
- for (int j = weight[0]; j <= bagweight; j++) {
- dp[0][j] = value[0];
- }
-
- for(int i = 1; i < weight.size(); i++) { // 遍历物品
- for(int j = 0; j <= bagweight; j++) { // 遍历容量
-
- // 如果装不下这个物品,那么就继承dp[i - 1][j]的值
- if (j < weight[i]) dp[i][j] = dp[i - 1][j];
-
-
- // 如果能装下,就将值更新为 不装这个物品的最大值 和 装这个物品的最大值 中的 最大值
- // 装这个物品的最大值由容量为j - weight[i]的包任意放入序号为[0, i - 1]的最大值 + 该物品的价值构成
- else
- {
- if(dp[i - 1][j]>dp[i - 1][j - weight[i]] + value[i])
- {
- dp[i][j] = dp[i - 1][j];
- isLoad[i] = 0;
- }
- else
- {
- dp[i][j] = dp[i - 1][j - weight[i]] + value[i];
- isLoad[i] = 1;
-
- }
- }
- }
- }
-
- //输出物品为个时 背包容量bagweight 的最大价值
- cout << "最大价值为:"<<dp[weight.size() - 1][bagweight] << endl;
- for(int i=0 ; i<n;i++)
- {
- if(isLoad[i]==0)
- cout<<"第"<<i+1<<"个物品没装"<<endl;
- else
- cout<<"第"<<i+1<<"个物品装了"<<endl;
- }
- }
-
- int main() {
-
- int n,bagweight;// bagweight代表背包的空间
- while(1) {
- cout<<"请输入物品数量和背包容量:"<<endl;
- cin >> n >> bagweight;
- solve( n, bagweight);
-
- }
-
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
二维动态规划的时间复杂度为O(n*bagweight),其中n是物品的数量,bagweight是背包的容量限制。在动态规划的过程中,需要填充一个二维的dp数组,该数组的大小为(n+1) * (bagweight+1)。每个位置的值需要通过比较和计算得到,因此总共需要进行(n+1) * (bagweight+1)次操作。 由于常数项在复杂度分析中不影响结果的趋势,我们可以简化表示为O(n*bagweight)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。