当前位置: 首页 > news >正文

代码随想录算法训练营第三十九天|01背包问题 二维、 01背包问题 一维、416. 分割等和子集

背包问题

01背包、完全背包、多重背包主要区别在于物品数量:01背包每个物品只有一个,完全背包物品有无限个,多重背包每个物品数量不同。

dp[i][j]表示编号[0,i]的物品任取放入容量为j的背包后,背包里物品的最大价值。

01背包问题 二维

#include <iostream>
#include <vector>
using namespace std;

int bag(vector<int>& space, vector<int>& value, int m, int n){
    // dp[i][j]表示在[0,i]物品任选,装入容量为j的背包的最大价值
    vector<vector<int>> dp(m, vector<int>(n+1));
    for(int j=space[0];j<=n;j++){
        dp[0][j] = value[0];// 初始化将物品0装入背包
    }
    for(int i=1;i<m;i++){
        for(int j=0;j<=n;j++){
            if(j<space[i]){
                // 容量不够装入第i个物品
                dp[i][j] = dp[i-1][j];
            }else{
                // 装入i vs 不装入i
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-space[i]]+value[i]);
            }
        }
    }
    return dp[m-1][n];
}

int main(){
    int m, n;
    cin>>m>>n;
    vector<int> space(m);
    vector<int> value(m);
    for(int i = 0;i<m;i++){
        cin>>space[i];
    }
    for(int i = 0;i<m;i++){
        cin>>value[i];
    }
    int res;
    res = bag(space, value, m, n);
    cout<< res <<endl;
    return 0;
}

①dp[i][j]表示在[0,i]物品中任选,装入容量为j的背包中获得的最大价值。

②对于每次选择只会存在两种结果:一是选择将物品i放入背包,二是将物品i不放入背包。放不放物品是根据价值来判断,因此,递推公式:dp[i][j]=max(dp[i-1][j], dp[i-1][j-space[i]]+value[i]),其中dp[i-1][j]表示不放入物品i,dp[i-1][j-space[i]]+value[i]表示放入物品i,j-space[j]表示腾出放入i的空间。

③初始化仅放入物品0的情况,只要容量大于物品0需要的空间,就选择放入物品0。

④遍历顺序从物品0遍历到物品m-1,背包容量从0到n。

01背包问题 一维

int bag(vector<int>& space, vector<int>& value, int m, int n){
    // dp[j]表示装入容量为j的背包的最大价值
    vector<int> dp(n+1);
    for(int i=0;i<m;i++){
        for(int j=n;j>=space[i];j--){
            // 装入i vs 不装入i
            dp[j] = max(dp[j], dp[j-space[i]]+value[i]);
        }
    }
    return dp[n];
}

一维背包主要是在之前二维背包的基础上,将二维dp数组压缩成一维dp数组,在二维dp数组的递推公式中,dp[i][j]里,i都依赖与i-1的值,因此可以将i-1的值都拷贝到i,以此压缩数组。基本思路如下:

①dp[j]表示容量为j的背包可装的最大价值。

②与二维类似,存在两种选择,放入物品和不放入物品。不放入物品的最大价值是:dp[j],不放入物品:dp[j-space[i]]+value[i]。

③初始化:背包容量为0的时候,dp[0]=0。

④遍历顺序需要从后往前遍历。假设背包容量5,物品3种,space=[1,3,4],value=[15,20,30]。对于物品0,如果正序遍历:

dp[1] = dp[1 - weight[0]] + value[0] = dp[0] + 15 = 15

dp[2] = dp[2 - weight[0]] + value[0] = dp[1] + 15 = 30

此时dp[2]就已经是30了,意味着物品0,被放入了两次,所以不能正序遍历。如果倒序遍历:

dp[2] = dp[2 - weight[0]] + value[0] = dp[1] + 15

dp[1] = dp[1 - weight[0]] + value[0] = dp[0] + 15

所以从后往前循环,每次取得状态不会和之前取得状态重合,这样每种物品就只取一次了。

416. 分割等和子集(中等)

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        if (sum % 2) {
            return false;
        }
        // 相当于背包容量为sum/2
        vector<int> dp(sum / 2 + 1);
        for (int i = 0; i < nums.size(); i++) {
            for (int j = sum / 2; j >= nums[i]; j--) {
                dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
            }
        }
        if (dp[sum / 2] == sum / 2)
            return true;
        else
            return false;
    }
};

本题难度中等。本题为01背包的应用,将数组分为两个相等的子集,先计算sum,可以看作背包容量为sum/2,需要使用nums的数字装满背包,且数字的价值和重量相同。最后判断一下容量为sum/2所装最大价值是不是sum/2。

相关文章:

  • 通往 AI 之路:Python 机器学习入门-概率与统计
  • 计算机毕业设计SpringBoot+Vue.js乐享田园系统(源码+文档+PPT+讲解)
  • 大模型时代下的数据标注革命:工具、挑战与未来趋势
  • Pywinauto Recorder: 将Windows操作转化为Python脚本,高效简易地实现操作自动化
  • 测试人员如何更好的跟踪BUG
  • 【C++】2.2.2 变量声明和定义的关系
  • devDependencies和dependencies的区别
  • 12、算法
  • 实时语义分割之FarSeeNet(2020)模型解析代码复现及滑坡检测实战
  • Linux----网络通信
  • WPS AI+office-ai的安装、使用
  • Linux查看TP6 command定时任务并重启
  • 一、Prometheus架构
  • table 拖拽移动
  • 广域互联网关键技术详解(GRE/LSTP/IPsec/NAT/SAC/SPR)
  • 文件上传复现
  • Office 2021 Mac Office办公
  • 【银河麒麟高级服务器操作系统实际案例分享】数据库资源重启现象分析及处理全过程
  • HTML5+CSS3+JS小实例:带缩略图的焦点图
  • vue组合式API中prop
  • 郑州seo线下培训/网站推广和优化的原因网络营销
  • 武汉网站开发软件开发/成都网站建设技术外包
  • 做网站和app多少费用/网络推广的重要性与好处
  • 手机网站在线客服/怎样在百度上发表文章
  • 住房和城乡建设部官网证件查询注册/嘉兴百度快照优化排名
  • 如何查看百度蜘蛛来过网站/北京网站制作400办理多少钱