本博客是记录作者学习最简单的01背包问题的笔记。

一、问题描述

标准的背包问题:有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

二、举例分析

背包:最大重量为4。

物品为:

  重量 价值
物品0 1 15
物品1 3 20
物品2 4 30

问背包能背的物品最大价值是多少?

问题代码定义如下:

vector<int> weight = {1, 3, 4}; //	物品重量
vector<int> value = {15, 20, 30};// 物品价值
int bagWeight = 4;// 背包最大重量

1、定义dp数组的含义

dp(i,j) 表示容量为j的背包从下标为[0-i]的物品里任意取物品的最大价值总和;

// 二维数组
vector<vector<int>> dp(weight.size(), vector<int>(bagWeight + 1, 0));

2、分析推导公式

我们可以按照放物品i和不放物品i两种情况来求出dp(i,j):

dp(i,j)=max(容量为j的背包不放物品i的最大价值,容量为j的背包放物品i的最大价值)。

容量为j的背包,里面不放物品i的最大价值是dp(i-1,j) ;

则容量为j-weight[i]的背包不放物品i的最大价值为dp(i-1,j-weight[i]);

则容量为j的背包里面放物品i的最大价值是dp(i-1,j-weight[i])+value[i];

所以我们的推导公式为: \(dp(i-1,j) =max(dp(i-1,j),dp(i-1,j-weight[i])+value[i])\)

3、初始化dp数组

容量j为0的背包放任意物品的最大价值都是0,则dp(i,0)=0;

根据推导公式我们也知道,我们也必须知道dp(0,i)的值,即编号0的物品放进容量为j的背包的最大价值

显然当 j < weight[0]时,即编号0的物品重量比背包还重,此时最大价值是0;

当 j >= weight[0]时,即编号0的物品重量比背包请,此时最大价值是value[0];

// 二维数组
vector<vector<int>> dp(weight.size(), vector<int>(bagWeight + 1, 0)); 
// 初始化
for (int j = weight[0]; j <= bagWeight; j++) {
    dp[0][j] = value[0];
}

初始化结果如图所示:

4、推导dp数组

// weight数组的大小 就是物品个数
    for(int i = 1; i < weight.size(); i++) { // 遍历物品
        for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
			// 如果背包容量j<当前物品重量weight[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]);
			}

        }
    }

5、打印dp数组

最后打印dp数组:

//打印dp数组
	for (int i = 0; i < weight.size(); i++){
		for (int j = 0; j <5; j++){
			cout<<dp[i][j]<<" ";
		}
		cout <<endl;
	}

打印结果如下:

0 15 15 15 15 
0 15 15 20 35 
0 15 15 20 35 

二、代码实现

void bag_problem(int bagWeight,vector<int> & weight,vector<int>& value) {
    // 二维数组
    vector<vector<int>> dp(weight.size(), vector<int>(bagWeight + 1, 0));

    // 初始化
    for (int j = weight[0]; j <= bagWeight; j++) {
        dp[0][j] = value[0];
    }

    // weight数组的大小 就是物品个数
    for(int i = 1; i < weight.size(); i++) { // 遍历物品
        for(int j = 0; j <= bagWeight; j++) { // 遍历背包容量
			// 如果背包容量j<当前物品重量weight[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]);
			}

        }
    }
	//打印dp数组
	for (int i = 0; i < weight.size(); i++){
		for (int j = 0; j <5; j++){
			cout<<dp[i][j]<<" ";
		}
		cout <<endl;
	}
}
int main()
{
   	cout << "Hello World"<<endl;
    int bagWeight = 4;// 背包重量
	vector<int> weight = {1, 3, 4}; //	物品重量
    vector<int> value = {15, 20, 30};// 物品价值
   	bag_problem(bagWeight,weight,value);
   	return 0;
}

使用滚动数组简化后:

void bag_problem(int bagWeight,vector<int> & weight,vector<int>& value) {
    // 一维数组及其初始化
    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]);
        }
    }
	//打印dp数组
    for (int j = 0; j <bagWeight + 1; j++){
        cout<<dp[j]<<" ";
    }
    cout <<endl;
}
int main()
{
   	cout << "Hello World"<<endl;
    int bagWeight = 4;// 背包重量
	vector<int> weight = {1, 3, 4}; //	物品重量
    vector<int> value = {15, 20, 30};// 物品价值
   	bag_problem(bagWeight,weight,value);
   	return 0;
}