0/1背包问题——从LeetCode题海中总结常见套路

news2024/11/15 8:10:50

目录

问题讨论

01背包问题公式

为什么状态压缩到一维时候需要逆序?

怎样求次数?

转化成最大和sum/2的01背包:LeetCode.416.分割等和子集

转化成最大和sum/2的01背包:LeetCode1049.最后一块石头的重量II

LeetCode.494.目标和

回溯超时

转换成01背包

LeetCode.474.一和零

阿里笔试原题:LeetCode.879.盈利计划


问题讨论

01背包问题公式

dp[j]为 容量为j的背包所背的最大价值,dp[j]可以通过dp[j - weight[i]]推导出来,dp[j - weight[i]]表示容量为j - weight[i]的背包所背的最大价值。dp[j - weight[i]] + value[i] 表示 容量为 j - 物品i重量 的背包 加上 物品i的价值。(也就是容量为j的背包,放入物品i了之后的价值即:dp[j])。

此时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]);

第一层是正序遍历,第二层逆序遍历,完整代码如下:

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;
}

为什么状态压缩到一维时候需要逆序?

压缩到一维时,要采用逆序。因为在一维情况下,是根据 dp[j] || dp[j - nums[i]]来推d[j]的值,如不逆序,就无法保证在外循环 i 值保持不变 j 值递增的情况下,dp[j - num[i]]的值不会被当前所放入的nums[i]所修改,当j值未到达临界条件前,会一直被nums[i]影响,也即是可能重复的放入了多次nums[i],为了避免前面对后面产生影响,故用逆序。 举个例子,数组为[2,2,3,5],要找和为6的组合,i = 0时,dp[2]为真,当i自增到1,j = 4时,nums[i] = 2,dp[4] = dp[4] || dp[4 - 2]为true,当i不变,j = 6时,dp[6] = dp [6] || dp [6 - 2],而dp[4]为true,所以dp[6] = true,显然是错误的。 故必须得纠正在正序情况下,i值不变时多次放入nums[i]的情况。

怎样求次数?

有的时候我们并不是要求出是否能装满容量为x的背包,dp[j] 表示:填满j(包括j)这么大容积的包,有dp[j]种方法。如果求凑成dp[j]总共有多少种方法呢?只需要把所有的 dp[j - nums[i]] 累加起来。用公式表达为:

dp[j] += dp[j - nums[i]]

一定要注意需要初始化:

dp[0] = 1

转化成最大和sum/2的01背包:LeetCode.416.分割等和子集

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sumNum = 0;
        for (auto num : nums)
            sumNum += num;
        // 和为奇数肯定不能够被分割
        if (sumNum % 2 ==1)
            return false;
        // 转换成和为sum/2的01背包问题
        int target = sumNum / 2;
        vector<int> dp(target + 1, 0);
        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]);
            }
        }
        return dp[target] == target;
    }
};

转化成最大和sum/2的01背包:LeetCode1049.最后一块石头的重量II

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        // 和416思路相似
        // 转换成01背包问题,求两堆石头的最小差值。由于石头总和为sum.则问题转换成了
        // 背包最多装sum / 2的石头,stones数组里有一大堆石头。求如何装能装下最多重量石头。
        int sum = 0;
        for (auto stone : stones)   sum += stone;
        int target = sum / 2;
        vector<int> dp(target + 1, 0);
        for (int i = 0; i < stones.size(); i++) {
            for (int j = target; j >= stones[i]; j--) {
                dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
            }
        }
        return sum - dp[target] - dp[target];
    }
};

LeetCode.494.目标和

回溯超时

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        // 通过递归列举出所有情况,超时
        help(nums, 0, 0, S);
        return count;
    }
private:
    int count = 0;
    void help (vector<int> nums, int i, int sum, int S) {
        if (i == nums.size()) {
            if (sum == S) {
                count++;
            }
        } else {
            help(nums, i + 1, sum + nums[i], S);
            help(nums, i + 1, sum - nums[i], S);
        }
    }
};

转换成01背包

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        // nums可以分成x1和x2两个部分,x1+x2=sum,x1-x2=target
        // 所以x1=(sum+target)/2
        int sum = std::accumulate(nums.begin(), nums.end(), 0);
        vector<int> dp(abs(target + sum) / 2 + 1, 0);
        if ((sum + target) % 2 != 0 || abs(target) > sum) {
            return 0;
        }
        dp[0] = 1;
        for (int i = 0; i < nums.size(); i++) {
            for (int j = (sum + target) / 2; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[(sum + target) / 2];
    }
};

LeetCode.474.一和零

其实是一个三维DP,但是状态压缩了,所以两次遍历都需要是逆序遍历。

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        // https://programmercarl.com/0474.%E4%B8%80%E5%92%8C%E9%9B%B6.html#%E6%80%9D%E8%B7%AF
        vector<vector<int> > dp(m + 1, vector<int> (n + 1, 0));
        for (auto str : strs) {
            int zeronum = 0;
            int onenum = 0;
            for (auto c : str) {
                if (c == '0') {
                    zeronum ++;
                } else {
                    onenum ++;
                }
            }
            for (int i = m; i >= zeronum; i--) {    // 从后向前遍历
                for (int j = n; j >= onenum; j--) {
                    dp[i][j] = max(dp[i][j], dp[i - zeronum][j - onenum] + 1);
                }
            }
        }
        return dp[m][n];
    }
};

阿里笔试原题:LeetCode.879.盈利计划

 暂时没有做出来,我好菜啊。。。

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

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

相关文章

微软文字转语音不能试用了,分享三个方法给大家!

最近很多小伙伴告诉我&#xff0c;微软文字转语音不能在线试用了&#xff0c;这是因为微软关闭了官方的使用页面&#xff0c;所以现在不能直接使用微软的网页版进行文字转语音了。 那么我们还有没有更好的方法去“白嫖”微软的文字转语音呢&#xff1f; 答案是肯定的&#xf…

初识NoSQL(一文读懂)

最近参加了Oracle的数据库培训&#xff0c;对NoSQL非常好奇&#xff0c;总结一下关于NoSQL的认识。 NoSQL是Not Only SQL&#xff0c;并不是去除掉SQL&#xff0c;泛指非关系型的数据库。关系&#xff0c;指关系模型&#xff0c;具体指同一个对象在不同属性上的值 以及 不同对…

一个AK/SK泄露检测的实现思路

01、简介 在企业上云的过程中&#xff0c;AK/SK泄露导致的数据泄露事件屡见不鲜。在企业混合云架构下&#xff0c;公有云和私有云都存在大量的AccessKey&#xff0c;如何有效地检测可能的AK/SK泄露事件&#xff0c;一直困扰着企业的安全人员。 本文提供了一种比较容易实现的思路…

UART协议学习——异步全双工串行通信方式

文章目录 前言一、简介1、优点2、缺点 二、数据格式三、波特率1、定义2、波特率和采样频率 四、常见接口电平1、TTL电平2、RS232&#xff08;负逻辑&#xff09;3、RS485 前言 2023.4.22 世界地球日 一、简介 UART&#xff1a;Universal Asynchronous Receiver/Transmitter&a…

Albert-Z-Guo/Deep-Reinforcement-Stock-Trading

深加固股票交易 该项目打算在投资组合管理中利用深度强化学习。框架结构的灵感来自Q-Trader。代理人的奖励是在每个行动步骤评估的未实现净利润&#xff08;意味着股票仍在投资组合中且尚未兑现&#xff09;。对于每一步的不作为&#xff0c;投资组合中都会增加负惩罚&#xf…

USMART 函数错误解决方法

身为电子工程师&#xff0c;看了马斯克的星舰飞船&#xff0c;真是太帅了&#xff1b; 深知一个良好的测试环境对产品性能的影响&#xff0c;对工作效率的提升。 小资源MCU调试代码的工具USMART 使用起来。 移植的文章网上有很多&#xff0c;但是对移植过程中使用错误的文章…

mybatisPlus拦截器使用demo

概述 顾名思义&#xff0c;就是一个拦截器&#xff0c;和springmvc的拦截器&#xff0c;servlet的过滤器差不多&#xff0c;就是在执行前拦了一道&#xff0c;里面可以做一些自己的事情。 平时用的mybatisPlus较多&#xff0c;直接以com.baomidou.mybatisplus.extension.plug…

VUE中使用element-china-area-data

使用element-china-area-data的中国省市区级联数据编写城市选择器。以下为解决效果图&#xff1a; &#xff08;1&#xff09;安装 npm install element-china-area-data -S &#xff08;2&#xff09;引入 import { regionData, CodeToText, TextToCode } from ‘element-ch…

LDO系列--LDO并联扩容

1、不能简单并联&#xff08;无法电流均衡&#xff09; 两个LDO的内部的带隙基准源(参考电压)&#xff0c;FET的特性&#xff0c;以及误差放大器的噪声不同(如失调电压)&#xff0c;实际LDO输出的目标电压依旧是有差异的。 这就导致了&#xff0c;LDO-High的目标输出电压高一些…

STM32F103基于标准库+I2C SSD1306仿数码管RTC时钟显示

STM32F103基于标准库I2C SSD1306仿数码管RTC时钟显示 ✨申明&#xff1a;本文章仅发表在CSDN网站&#xff0c;任何其他网站&#xff0c;未注明来源&#xff0c;见此内容均为盗链和爬取。 &#x1f341;对于文中所提供的相关资源链接将作不定期更换。 &#x1f4fa;显示效果&a…

UWB芯片DW300之CRC模式介绍及代码实现

SPI CRC模式 当启用SPI CRC模式时,可以为SPI传输提供循环冗余校验序列的额外保护。这种操作模式在默认情况下是禁用的,但可以通过SYS_CFG寄存器中的SPI_CRCEN位启用(和禁用)。 虽然SPI CRC检查在主机微处理器必须为每个SPI写入和读取事务计算CRC的附加软件开销方面有缺点,但…

SOFA Weekly|SOFARPC 5.10.0 版本发布、SOFA 五周年回顾、Layotto 社区会议回顾与预告...

SOFA WEEKLY | 每周精选 筛选每周精华问答&#xff0c;同步开源进展 欢迎留言互动&#xff5e; SOFAStack&#xff08;Scalable Open Financial Architecture Stack&#xff09;是蚂蚁集团自主研发的金融级云原生架构&#xff0c;包含了构建金融级云原生架构所需的各个组件&am…

【Mysql】分库分表

【Mysql】分库分表 文章目录 【Mysql】分库分表1. 介绍2. 拆分策略2.1 垂直拆分2.1.1 垂直分库2.1.2 垂直分表 2.2 水平拆分2.2.1 水平分库2.2.2 水平分表 3. MyCat3.1 概述 1. 介绍 采用单数据库进行数据存储存在以下瓶颈&#xff1a; IO瓶颈&#xff1a;热点数据太多&#x…

项目管理必备!20个实用技巧全掌握!

即使在最完美的条件下&#xff0c;管理一个项目也是很困难的。 ​项目管理的成败好坏与优秀项目团队密不可分的,建设一个好的团队将会更团结、更坚强、更具有竞争力, 更能适应无限变化的环境。 ​不幸的是&#xff0c;还是有很多项目经理实质上没有没有总结出自己思维方法和运…

进程状态

理念上的状态 新建 子面意思运行 task_struct在运行队列中排队&#xff0c;就叫做运行态阻塞 等待非CPU资源就绪挂起 当内存不足的时候&#xff0c;OS通过适当的置换进程的代码和数据到磁盘&#xff0c;进程的状态就叫做挂起退出 子面意思 实际上的状态 …

ARM Coresight 及 DS-5 介绍 5 - DS-5 断点设置及常用Debug 命令

文章目录 1.1 DS-5 Debug 方法梳理1.2.1 DS-5 设置断点 Debug1.2.2 DS-5 常用 Debug 命令 1.1 DS-5 Debug 方法梳理 通常在调试过程中需要打断点来进行单步调试&#xff0c;这个时候可以按照下面步骤来进行&#xff1a; 在使用 DS-5 Debug 之前需要先 load 所编译的 elf 文件&…

【css】使用css实现提示框各种弹出效果。

简言 最近工作编写页面时&#xff0c;需要有一个提示框从下到上弹出的效果。 冥想了一下&#xff0c;实现了出来。 记录下实现思路。 实现思路 实现步骤如下&#xff1a; 编写样式。 首页要有承载内容的容器&#xff08;box)。外层在套一个包装盒子&#xff08;用来进行定位…

超详细的ubuntu安装opencv2.0//test ok

目录 1. 首先确保在Ubuntu上已经安装了cmake和make 1.1 安装make 1.2 安装cmake 2 安装依赖环境 3 下载opencv源码 4 编译源码并安装 4.1 进入opencv源码目录中&#xff0c;新建build文件夹 4.2 进入build文件夹&#xff0c;打开终端使用cmake生成makefile 4.3 安装ope…

Windows环境下实现设计模式——迭代器模式(JAVA版)

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天总结一下Windows环境下如何编程实现迭代器模式&#xff08;设计模式&#xff09;。 不知道大家有没有这样的感觉&#xff0c;看了一大堆编程和设计模式的书&#xff0c;却还是很难理解设计模式&#xff…

轻松掌握k8s的kubectl使用命令行操作Ingress知识点03

1、Ingress将所有Service统一网关入口 底层也是使用了nginx&#xff0c;所以使用Ingress才是整个项目的统一入口。 官网地址&#xff1a;https://kubernetes.github.io/ingress-nginx/ 1、安装 先下载安装文件 wget https://raw.githubusercontent.com/kubernetes/ingress-…