0-1背包问题简介

关于0-1背包问题,相信大家都不陌生,这是一个典型的动态规划问题,但是平时上课对这些的理解可能会不太够,我也是如此,所以参考了一些资料,对0-1背包问题做了一个简单的总结。还是那句话

本人很菜,不喜勿喷,谢谢!

0-1背包问题其实就是这么个情况:我有一个限制重量的背包,然后有一些有价值且有重量的物品,我要往里面装物品,现在需要找一个方案,使我的背包尽可能地装满,并且价值最高。

思路简介

首先,我们简单考虑一下贪心算法,既然是贪心,就必须找到局部的最优子结构,那么,什么是局部最优子结构呢? 答案应该是找单位价值最大的物品往里面装,但是,可以发现,装到最后,背包里所装物品可能不是最优解。

以背包容量为4,物品重量分别为1,3,4,对应的价值为8,20,30为例。

第一次的局部最优解肯定是装重量为1、价值为8的物品;
第二次由于背包容量问题,我只能装重量为3、价值为20的物品,那么,此时的背包总价值为28,显然会有一个30的更优解。

所以,贪心算法肯定不行,但是动态规划是可以的,网上有很多讲解动态规划的大佬,我这就不班门弄斧了,推荐一个《代码随想录》,B站上也有他的教程,很nice!

这也说明了用动态规划求解的问题不能用贪心算法来求解,那么反过来了?答案我留到最后,至于为什么,请自行查找。

使用二维数组的情况

先直接上代码。

#include<iostream>
#include<vector>
//bits/stdc++.h文件在实际开发中不要使用
//在VScode中似乎已经限制了bits/c++.h的使用。
// #include<bits/stdc++.h>
using namespace std;

//0-1背包问题
void test_2_wei_bag_problem1(){
    vector<int> weight ={1,3,4};
    vector<int> value = {15,20,30};
    int bagweight = 4;
    //二维数组 
    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++){
            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]);

        }
    }

    cout<<dp[weight.size()-1][bagweight]<<endl;
}


int main()
{
    test_2_wei_bag_problem1();
    
    //system("pause");
    //如果需要在VScode中运行出命令控制台,请加上。
}

代码中已经有比较详细的注释了,在此只提以下两点:

1.dp[i] [j] 表示背包重量为j时,装有i个物品时的总价值;
2.递推关系式为dp [i] [j] = max(dp [i-1] [j],dp [i-1] [j-weight[i]]+value[i])。

使用一维数组的情况

直接上代码。

#include <iostream>
#include <vector>

using namespace std ;

void test_0_1_bag_problem(){
    vector<int> weight = {1,3,4};
    vector<int> value = {15,20,30};

    int bagweight = 4;

    vector<int> dp(bagweight+1 ,0); 
    // dp[j]数组 表示重量为j的的背包里最多可装物品的价值
    // 初始化为0,避免大数覆盖遍历结果。
    for(int i=0;i<weight.size();i++){
        for(int j=bagweight;j>=weight[i];j--) //循环条件: 当包中还可以将第i个物品装下为止。
        {
            dp[j] = max(dp[j],dp[j-weight[i]] + value[i]); //递推关系式 dp[j] = max(dp[j] , dp[j-weight[i]] + value[i])
        } 
    }
    cout<<dp[bagweight]<<endl;

}

int main(){
    test_0_1_bag_problem();
    //system("pause");
    return 0;
}

同样也是两个要点:

1. dp[j] 表示重量为j的的背包里最多可装物品的价值;
2. 递推关系 dp[j] = max(dp[j],dp[j-weight[i]] + value[i])。

这里还需要注意的是,为了防止从前往后遍历的情况下,大数覆盖递推结果及同类物品被重复添加的问题,采用了从后往前的遍历方法。及dp[j] = max(dp[j],dp[j-weight[i]] + value[i])。

能用贪心算法求解的问题是可以用动态规划来求解的,但是耗费的时间会更多,所以没必要这样做。
参考于《代码随想录》

@AHU-IAI-Threemen_ZL