代码随想录训练营第42天|01背包问题、LeetCode 416. 分割等和子集

news2024/12/23 22:15:33

参考

代码随想录

01背包问题

01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2至Wn,与之相对应的价值为P1,P2至Pn。01背包是背包问题中最简单的问题。01背包的约束条件是给定几种物品,每种物品有且只有一个,并且有权值和体积两个属性。在01背包问题中,因为每种物品只有一个,对于每个物品只需要考虑选与不选两种情况。如果不选择将其放入背包中,则不需要处理。如果选择将其放入背包中,由于不清楚之前放入的物品占据了多大的空间,需要枚举将这个物品放入背包后可能占据背包空间的所有情况。

上面的体积也可能换成是重量,本质上是一样的。

在这里插入图片描述

二维dp数组01背包

  1. 确定dp数组及其下标的含义
    这里将dp数组定义为二维,dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
    在这里插入图片描述
  2. 确定递推公式
    可以有两个方向推出dp[i][j]:
  • 不能放入物品i:dp[i][j] = d[i-1][j],这种情况说明不能放入物品i,放入物品i会超出背包的最大承载重量,所以背包承载重量为j时价值最大总和与不放入物品i时相等。
  • 能放入物品i:dp[i][j] = dp[i - 1][j - weight[i]] + value[i],这里放入物品i后的背包承载重量为j,因此放入物品i之前的背包的承载重量是j-weight[i],再加上该物品的价值就得到了此递推公式

综合起来,要保证价值总和最大,因此递推公式为:

dp[i][j] = max(dp[i][i-1], dp[i - 1][j - weight[i]] + value[i])
  1. dp数组初始化
    当背包承载重量j为0时,背包里不能放任何物品,因此价值总和为0。
    由递推公式可以看出,dp[i][j]和dp[i-1][j]有关系,因此i=0是需要进行初始化,i=0意味着放入物品0。所以当背包承载重量j < weigft[0]时,dp[0][j] = 0;当j >= weight[0]时,dp[0][j] = value[0].
for (int j = 0 ; j < weight[0]; j++) {  // 当然这一步,如果把dp数组预先初始化为0了,这一步就可以省略
    dp[0][j] = 0;
}
// 正序遍历
for (int j = weight[0]; j <= bagweight; j++) {
    dp[0][j] = value[0];
}

在这里插入图片描述

  1. 确定遍历顺序
    可以先遍历背包,也可以先遍历物品。
    先遍历物品:
// 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]);

    }
}

先遍历背包:

// weight数组的大小 就是物品个数
for(int j = 0; j <= bagweight; j++) { // 遍历背包容量
    for(int i = 1; i < weight.size(); 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]);
    }
}
  1. 举例推导dp数组
    在这里插入图片描述
    整体代码实现如下:
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();
}

一维dp数组(滚动数组)01背包

对于背包问题其实状态都是可以压缩的。

在使用二维数组的时候,递推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

其实可以发现如果把dp[i - 1]那一层拷贝到dp[i]上,表达式完全可以是:dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);

与其把dp[i - 1]这一层拷贝到dp[i]上,不如只用一个一维数组了,只用dp[j](一维数组,也可以理解是一个滚动数组)。

  1. 确定dp数组及其下标的含义
    在一维dp数组中,dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。
  2. 确定递推公式
    dp[j]有两个选择,一个是取自己dp[j] 相当于 二维dp数组中的dp[i-1][j],即不放物品i,一个是取dp[j - weight[i]] + value[i],即放物品i,指定是取最大的,毕竟是求最大价值,所以递推公式为:
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
  1. dp数组初始化
    当j = 0时,背包里不能放任何物品,所以最大价值总和为0,即dp[0] = 0。dp数组在推导的时候一定是取价值最大的数,如果题目给的价值都是正整数那么非0下标都初始化为0就可以了。
  2. 确定遍历顺序
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]);

    }
}
  1. 举例推导dp数组
    在这里插入图片描述
    完整的代码实现如下:
void test_1_wei_bag_problem() {
    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]);
        }
    }
    cout << dp[bagWeight] << endl;
}

int main() {
    test_1_wei_bag_problem();
}

LeetCode 416.分割等和子集

只有确定了如下四点,才能把01背包问题套到本题上来。

  • 背包的体积为sum / 2
  • 背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值
  • 背包如果正好装满,说明找到了总和为 sum / 2 的子集。
  • 背包中每一个元素是不可重复放入。

以上分析完,我们就可以套用01背包,来解决这个问题了。

  1. 确定dp数组及其下标的含义
    本题中每一个元素的数值即是重量,也是价值。01背包套到本题,dp[j]表示 背包总容量(所能装的总重量)是j,放进物品后,背的最大重量为dp[j]。
  2. 确定递推公式
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
  1. dp数组初始化
    从dp[j]的定义来看,首先dp[0]一定是0。如果如果题目给的价值都是正整数那么非0下标都初始化为0就可以了,如果题目给的价值有负数,那么非0下标就要初始化为负无穷。
  2. 确定遍历顺序
for(int i = 0; i < nums.size(); i++) {
    for(int j = target; j >= nums[i]; j--) { // 每一个元素一定是不可重复放入,所以从大到小遍历
        dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
    }
}
  1. 举例推导dp数组
    在这里插入图片描述
    完整的代码实现如下:

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;

        // dp[i]中的i表示背包内总和
        // 题目中说:每个数组中的元素不会超过 100,数组的大小不会超过 200
        // 总和不会大于20000,背包最大只需要其中一半,所以10001大小就可以了
        vector<int> dp(10001, 0);
        for (int i = 0; i < nums.size(); i++) {
            sum += nums[i];
        }
        // 也可以使用库函数一步求和
        // int sum = accumulate(nums.begin(), nums.end(), 0);
        if (sum % 2 == 1) return false;
        int target = sum / 2;

        // 开始 01背包
        for(int i = 0; i < nums.size(); i++) {
            for(int j = target; j >= nums[i]; j--) { // 每一个元素一定是不可重复放入,所以从大到小遍历
                dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
            }
        }
        // 集合中的元素正好可以凑成总和target
        if (dp[target] == target) return true;
        return false;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/66741.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java计算机毕业设计ssm网上拍卖系统vdum4(附源码、数据库)

java计算机毕业设计ssm网上拍卖系统vdum4&#xff08;附源码、数据库&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。…

MySql分库分表

问题分析&#xff1a; 随着互联网及移动互联网的发展&#xff0c;应用系统的数据量也是成指数式增长&#xff0c;若采用单数据库进行数据存储&#xff0c;存在以下性能瓶颈&#xff1a; 1、IO瓶颈&#xff1a;热点数据太多&#xff0c;数据库缓存不足&#xff0c;产生大量磁盘…

小快轻准,5分钟自助上线,中小型制造企业数字化转型

中小企业作为国民经济的重要组成部分&#xff0c;占据我国企业数量的90%以上。 继《中小企业数字化赋能专项行动方案》发布后&#xff0c;工信部近日印发《中小企业数字化转型指南》&#xff0c;提出14条具体举措&#xff0c;为中小企业明确数字化转型路径。 地方政府也纷纷出…

Linux系统管理、运行级别、关闭防火墙

目录 一、Linux服务管理 1.1 基本概念 1.2 systemctl&#xff08;centos7&#xff0c;很重要&#xff09; 1.2.1 基本语法 1.2.2 查看服务 1.2.3 防火墙操作 1.3 systemctl 设置后台服务的自启配置 二、系统运行级别 2.1 运行级别 2.2 查看当前运行级别 2.2修改当前运行…

8年三届世界杯,8年前端开发,梅西一共踢没了我八千八

转眼2022年即将过去&#xff0c;我已经做了那么久的开发了&#xff0c;一路走来&#xff0c;不断的工作&#xff0c;换工作&#xff0c;找工作&#xff0c;不断的学习新知识。 同时也看着梅西参加了3届世界杯了&#xff0c;逝者如斯夫&#xff0c;不舍昼夜啊。 在这8年里&#…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java焦作旅游网站q5msq

首先选择计算机题目的时候先看定什么主题&#xff0c;一般的话都选择当年最热门的话题进行组题&#xff0c;就比如说&#xff0c;今年的热门话题有奥运会&#xff0c;全运会&#xff0c;残运会&#xff0c;或者疫情相关的&#xff0c;这些都是热门话题&#xff0c;所以你就可以…

Elasticsearch Data Stream 数据流使用

本文是《Elasticsearch索引生命周期管理ILM》中数据流索引补充篇&#xff0c;文章地址如下&#xff1a; https://mp.weixin.qq.com/s/ajhFp-xBU1dJm8a1dDdRQQ 并且在另一片Elasticsearch的进阶使用-动态模版中也提到了相关数据流索引的内容&#xff0c;有兴趣的可以回过头看一下…

知乎热议的 Java 面试成神笔记,下载量已过90k,面试神技.exe进程已启动

在某论坛上面对于“中国程序员数量是否已经饱和或者过剩”这个话题很火&#xff01; 今年大家都有一个共同的感受&#xff1a;工作不好找&#xff0c;薪资不理想&#xff0c;面试造火箭。 其实&#xff0c;由于不仅是因为今年受疫情影响&#xff0c;很多公司经营不下去&#xf…

Java中的类和对象(Java系列4)

目录 前言&#xff1a; 1.什么是面向对象 2.类的定义和使用 3.this引用 3.1this引用的概念 3.2this引用的特性 3.3this的使用 4.构造方法 4.1构造方法概念 4.2构造方法的特性 5.默认初始化 6.就地初始化 7.封装 8.封装扩展之包 8.1为什么对提出包的概念呢&#x…

前端开发需要会什么?先掌握这三大核心关键技术

前端工程师&#xff0c;如何创造/制作一个网站应用呢&#xff1f; 为了更好的让你理解&#xff0c;我们简单地做一个类比&#xff1a;动画制作者在创造人物的时候&#xff0c;需要从构图、着色和动画效果上入手&#xff0c;让人物有骨、有血&#xff0c;灵性生动。 而如果将一…

长安链共识模块优化中的“精益求精”

长安链在v2.3.0版本中对共识模块进行了优化&#xff0c;主要包括优化TBFT消息机制并将异步发送信息功能进行了拆分&#xff0c;独立设计了一致性引擎模块。优化后共识模块整体逻辑更清晰、共识更高效。 TBFT消息机制优化背景 在长安链的V2.3.0版本的TBFT共识算法中&#xff0…

W易NEWS login逆向分析

内容仅供参考学习 欢迎朋友们V一起交流&#xff1a; zcxl7_7 目标 网址&#xff1a;案例地址 分析 首先进行一次登录&#xff0c;获取流程 在这个流程中可以看到dl/zj/mail/l这个接口返回了新的cookie&#xff0c;可以猜测这个是登录核心&#xff1b;dl/zj/mail…

通达信接口公式怎样进行破解?

如果我们想对通达信接口的公式进行破解该怎么做呢&#xff1f;今天我们来风险一下方法&#xff0c;但是有两个前提&#xff1a; 1:不是使用winhex&#xff0c;而是使用ollydbg来破解&#xff1b; 2:只是用户公式修改密码有效。对于导入密码无效。对于系统公式密码无效&#xff…

c语言:关键字(一)

关键字一.古老的关键字—auto1.全局变量与局部变量2.auto的使用二.最快的关键字—register一.古老的关键字—auto 1.全局变量与局部变量 在花括号里定义的变量就是局部变量&#xff0c;反之就是全局变量 这里举个例子 这时我们在进行编译时会报错告诉我们y没定义 这其实就是…

redis常用数据类型以及命令详细介绍

查询redis的命令&#xff1a;http://www.redis.cn/commands.html Redis键(key) keys *查看当前库所有key (匹配&#xff1a;keys *1) exists key判断某个key是否存在 type key 查看你的key是什么类型 del key 删除指定的key数据 unlink key 根据value选择非阻塞删除 仅将keys…

计算机组成原理(二)运算方法和运算器

目录数制与编码字符和字符串的表示方法ASCII码汉字编码定点数和浮点数的表示定点数表示方法浮点数表示方法定点数和浮点数的运算定点数的运算浮点数的运算运算器的组成算数逻辑单元&#xff08;ALU&#xff09;算数单元逻辑单元内部总线运算器的基本结构数制与编码 字符和字符…

【动态规划】爬楼梯爬的不仅仅是楼梯

深度思考爬楼梯问题&#xff0c;抽取一般过程&#xff0c;目标是对其变式题也能认出并且求解 一、题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; https://leetcode.cn/problems/climbin…

[附源码]Python计算机毕业设计SSM建筑工程管理系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

.NET 分页帮助类

一直以来&#xff0c;分页是一个非常常见的需求&#xff0c;以前也用过很多的分页控件&#xff0c;比如AspNetPager&#xff0c;现在自己参照之前用过的&#xff0c;自己写了一个非常简单实用的分页实现方法。 先来看看应用效果&#xff1a; 如图&#xff0c;定义了是前10页&…

论文投稿指南——中文核心期刊推荐(电子、通信技术)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384;&#x1f388; 核心期刊在国内的应用范围非常广&#xff0c;核心期刊发表很多是国内作者晋升中的硬性要求&#xff0c;在…