01背包
有N件物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次
二维dp数组
dp[i] [j]的含义:从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
递归公式:dp[i] [j] = max(dp[i - 1] [j], dp[i - 1] [j - weight[i]] + value[i])
重量为j的背包能放入的最大价值和则有两种情况:
1、当前背包容量j<weight[i] , 当前物品的重量weight[i],说明当前背包容量放不下重量为weight[i] 的物品,不能得到该物品的价值, 所以dp[i] [j] = dp[i - 1] [j], 即存放了之前物品而没有存放当前物品的最大价值和;
2、j>=weight[i], 当前背包容量j能放下重量为weight[i]的物品,此时会考虑放不放的问题,因为放入该物品后的价值可能小于存放了之前物品而没有存放当前物品的价值,所以需要从两者中取最大值dp[i] [j] = max(dp[i - 1] [j], dp[i - 1] [j - weight[i]] + value[i])。
dp[i -1] [j - weight] 表示在重量为weight[ 0~i ]中的物品取任意件数,重量为j - weight的背包能获得的最大价值和
dp[i - 1] [j - weight[i]] + value[i] 可以理解成在能够存放当前重量物品(价值为value[i])的前提下还可以获得j - weight重量物品的价值
#include<bits/stdc++.h>
using namespace std;
int main(){
vector<int> weight={1,3,4};
vector<int> value={15,20,30};
int bagweight=4;
vector< vector<int> > dp(weight.size()+1,vector<int>(bagweight+1,0));
//倒序初始化,倒序才能体现物品只能用一次的限制
for(int j=bagweight;j>=weight[0];j--){
dp[0][j]=dp[0][j-weight[0]]+value[0];
}
//先遍历物品,再遍历背包容量
for(int i=1;i<weight.size();i++){
for(int j=1;j<=bagweight;j++){
if(j<weight[i]){
dp[i][j]=dp[i-1][j];
}//数据用不到,但可以展现dp数组的变化,打印完整的dp数组
else{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
}
}
}
//打印dp数组
for(int i=0;i<weight.size();i++){
for(int j=0;j<=bagweight;j++){
cout<<dp[i][j]<<" ";
}
cout<<endl;
}
}
更容易理解的初始化:
for (int j = 0; j <= bagWeight; j++) {
if (weight[0] <= j) {
dp[0][j] = value[0];
}
}
滚动数组一维dp
dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
假设物品价值都是大于0的,所以dp数组初始化的时候,都初始为0就可以了。
倒叙遍历是为了保证物品i只被放入一次
对于二维dp,dp[i][j]都是通过上一层即dp[i - 1] [j]计算而来,本层的dp[i] [j]并不会被覆盖!
先遍历背包再遍历物品时dp[j] 表示的是当前容量 j 能存储的单个 物品i的最大价值, 而不是dp数组含义中的从0 - i任取任意件物品的最大价值了。
如果遍历背包容量放在外层,那么每个dp[j]就只会放入一个物品,即:背包里只放入了一个物品,一趟比较求出来的最大价值就只能是单个物品的价值。
以下是先遍历背包再遍历物品的结果,最后dp[4]=30.
for(int j=bagweight;j>=weight[i];j--){
for(int i=0;i<weight.size();i++){}
}
一维dp最终得到结果如下:
以下直接输出辅助理解
for(int i=0;i<weight.size();i++){
for(int j=bagweight;j>=weight[i];j--){//倒序遍历,防止覆盖
dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
cout<<dp[j]<<" ";
}
cout<<endl;
}
执行结果:
一维dp代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
vector<int> weight={1,3,4};
vector<int> value={15,20,30};
int bagweight=4;
vector<int> dp(bagweight+1,0);//初始化
//一定先遍历物品,再遍历背包容量,否则相当于每趟只放一个物品
for(int i=0;i<weight.size();i++){
for(int j=bagweight;j>=weight[i];j--){//倒序遍历,防止覆盖
dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
}
}
return 0;
}