给定n种物品和一个背包,物品i的重量是wi,其价值为 v i,背包的容量为C。
背包问题是如何选择装入背包的物品,使得装入背包中物品的总价值最大? 如果在选择 装入背包的物品时,对每种物品i只有两种选择:装入背包或不装入背包,即不能将物品i装入背包多次,也不能只装入物品i的一部分,则称为0/1背包问题
在0/1背包问题中首先需要考虑的限制条件就是:如果当前的第 i 个物品的重量大于了背包的剩余容量,那么就不能装入。
如果能装入,那我们再考虑到底要不要装入?也就是从 “装入” 和 “不装入”两个方面来获得背包的最大价值。
因此定义dp数组:dp [ i ] [ j ]
表示当背包的容量为 j 的时候,装入前 i 个物品可以获得的最大价值。
上述对 i 的解释是错误的,如果能装入前 i 个,那么总价值已经确定了,就没必要算了。
所以正确的解释是:
表示当背包的容量为 j 的时候,对于前 i 个物品,可以获得的最大价值。也就是可装可不装。
即获得我们本次算出的最优解,不仅是得出结果。
我们用一个一维数组 isLoad[ i ] 来表示物品 i 有没有被装入。值为1表示装入,为0表示未装入。
为了确定装入背包的具体物品,我们从 dp[ n ] [ w ] 往前推。如果dp[ n ] [ w ] > dp [ n - 1] [ w ],则说明第n个物品肯定被装入了背包,因为此时背包的价值更大。否则物品n肯定没有被放入背包。一个if-else就可以解决问题。
注意点在下面代码中,weight和value数组的下标是从1开始的,对应第 i 个物品。0下标没有用,数值用0代替。
代码#include
#include
using namespace std;
int knapSack(int n, int w, int weight[], int value[], int isLoad[]) {
// 多开辟的一个空间是为了容量为0和不装东西的情况
int dp[n+1][w+1]; // 定义dp数组 表示当容量为w的时候,对于前i个物品可以获得的最大价值
for(int i = 0 ; i <= n; i++) dp[i][0] = 0; // 容量为0
for(int i = 0 ; i <= w ; i++) dp[0][i] = 0; // 不装东西
// 动态规划
for(int i = 1 ; i <= n; i++) {
for(int j = 1 ; j <= w ; j++) {
// 准备放第i个物品
if(j < weight[i]) { // 背包的剩余容量不够了 就不放
dp[i][j] = dp[i-1][j]; // 不装
} else {
dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]);
}
}
}
// 获得装入背包的物品 存放在isLoad数组中
int j = w;
for(int i = n ; i >= 1 ; i--) { // 第i个物品
if(dp[i][j] > dp[i-1][j]) {
isLoad[i] = 1;
j -= weight[i];
}
else {
isLoad[i] = 0;
}
}
return dp[n][w];
}
int main() {
int n = 3;
int w = 4;
int weight[4] = {0,2,1,3};
int value[4] = {0,4,2,3}; // 下标从1开始 0下标不用 直接设置为0
int isLoad[4];
int maxValue = knapSack(n,w,weight,value,isLoad);
cout<<"最大价值是"<<maxValue;
for(int i = 1; i <=n ; i++) cout<<isLoad[i]<< " ";
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)