(算法设计与分析)第三章动态规划-第二节:动态规划之背包类型问题

news2025/7/18 8:37:30

文章目录

  • 一:01背包问题
    • (1)题目描述
    • (2)解题思路
    • (3)完整代码
  • 二:分割等和子集(01背包变形)
    • (1)题目描述
    • (2)解题思路
    • (3)完整代码
  • 三:完全背包问题
    • (1)题目描述
    • (2)解题思路
    • (3)完整代码

一:01背包问题

(1)题目描述

给你一个可装载重量为 W 的背包和 N 个物品,每个物品有重量价值两个属性。其中第 i 个物品的重量为 wt[i],价值为 val[i],现在让你用这个背包装物品,最多能装的价值是多少?
在这里插入图片描述

举个简单的例子,输入如下

N = 3, W = 4
wt = [2, 1, 3]
val = [4, 2, 3]

算法返回 6,选择前两件物品装进背包,总重量 3 小于 W,可以获得最大价值 6

(2)解题思路

①:考虑状态和选择是什么

  • 状态:由于物品不断装入背包,所以状态有两个,分别为背包容量可选择的物品
  • 选择:对于每件物品,你的选择就是要么装进背包要么不装进背包(也就是0和1)

伪代码如下

在这里插入图片描述

②:明确table数组定义:状态有两个,所以要定义成一个二维表。table[i][w]表示,对于前i个物品,当前背包的容量为w,此种情况下可以装入的最大价值为table[i][w]

  • 例如table[3][5] = 6,其含义为对于给定的一系列物品中,若只对前 3 个物品进行选择,当背包容量为 5 时,最多可以装下的价值为 6
  • 最终返回table[N][W]
  • 最简单情况:没有物品或背包没有空间时,能装的最大价值为0,即table[0][...]=table[...][0]=0

伪代码如下
在这里插入图片描述

③:根据选择,思考转移的逻辑:也即如何把选择用代码描述出来

  • 如果没有把第i个物品放在背包:很显然,既然没有把第i个放进去,那么价值量不会增加,状态也不会变化,也即table[i][w]==table[i-1][w]
  • 如果把第i个物品放入了背包:既然放入了背包,那么此状态的容量一定会减少wt[i],而价值则会增加val[i],因此dp[i][w]==dp[i-1][w-wt[i-1]]+val[i-1]

需要注意的是i是从1开始的,因此valwt的索引中i-1表示第i个物品。所以dp[i][w]==dp[i-1][w-wt[i-1]]+val[i-1]表示如果把第i个物品装入了,就要寻找剩余重量w-wt[i-1]限制下的最大价值,加上第i个物品的价值val[i-1]

伪代码如下

在这里插入图片描述

(3)完整代码

int knapsack(int W, int N, vector<int> &wt, vector<int> &val){
    //状态有两个,所以建一个二维数组,注意让其索引从1开始
    //最简单情况:table[0][....]=table[....][0] = 0,表示没有物品或背包没有空间时,能装的价值为0
    vector<vector<int>> table(N+1, vector<int>(W+1, 0));

    //填表过程
    for(int i = 1; i <= N; i++){
        for(int w = 1; w <= W; w++){
            //情况1:若果当前背包容量不足以装下这个物品,则不装入,那么table只能继承前一个
            //w - wt[i-1]表示如果把重量为wt[i-1]的物品装入后的重量
            if(w - wt[i-1] < 0){
                table[i][w] = table[i-1][w];
            }else{
                //情况2:可以装入,那么就选择最大价值
                //
                table[i][w] = max(
                        table[i-1][w-wt[i-1]] + val[i-1],
                        table[i-1][w]
                        );
            }
        }
    }

    return table[N][W];
}

int main(){
    int W = 4;
    int N = 3;
    vector<int> wt ={2, 1, 3};
    vector<int> val ={4, 2, 3};

    cout << knapsack(W, N, wt, val) << endl;
}

在这里插入图片描述

二:分割等和子集(01背包变形)

(1)题目描述

  • LeetCode416:分割等和子集

输入一个只包含正整数的非空数组 nums,请你写一个算法,判断这个数组是否可以被分割成两个子集,使得两个子集的元素和相等

举个简单的例子,输入如下

nums = [1,5,11,5]

算法返回 true,因为 nums 可以分割成 [1,5,5][11] 这两个子集

(2)解题思路

此题可以转化为背包问题去做,背包问题是这样说的

你一个可装载重量为 W 的背包和 N 个物品,每个物品有重量价值两个属性。其中第 i 个物品的重量为 wt[i],价值为 val[i],现在让你用这个背包装物品,最多能装的价值是多少?

故问题转化为:给你一个可装载重量为sum/2的背包和N个物品,每个物品重量为nums[i],现在让你装物品,问是否存在一种装法,可以恰好把背包装满


①:考虑状态和选择是什么

  • 状态:由于物品不断装入背包,所以状态有两个,分别为背包容量可选择的物品
  • 选择:对于每件物品,你的选择就是要么装进背包要么不装进背包(也就是0和1)

②:明确table数组定义:状态有两个,所以要定义成一个二维表。table[i][j]=x表示,对于前i个物品,当前背包的容量为j时,若xtrue,则说明恰好可以把背包装满,反之若xfalse则表示不可以恰好把背包装满

  • 例如table[3][5] = true,其含义为对于容量为9的背包,如果只用前4个物品,可以有一种方法将背包装满(对本题来说,就是对于给定的集合,如果只对前4个数字进行选择,存在一个子集的和可以恰好凑出9)
  • 最终返回table[元素个数][sum/2]
  • 最简单情况:,table[...][0]=true表示背包没有空间时相当于装满了;table[0][...]=false表示没有元素时肯定没办法装满背包

③:根据选择,思考转移的逻辑:也即如何把选择用代码描述出来

  • 如果没有把第i个物品放在背包(没有把nums[i]算入子集):同理,此时取决于上一个状态,即table[i][j]==table[i-1][j]
  • 如果把第i个物品放入了背包(把nums[i]算入子集):同理,取决于状态table[i-1][j-nums[i-1]]

(3)完整代码

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;
        for(auto e : nums){
            sum += e;
        }
        //和为奇数时是不可能分开的
        if(sum % 2 != 0){
            return false;
        }
        sum /= 2;
        int n = nums.size();
        //默认全为false
        vector<vector<bool>> table(n+1, vector<bool>(sum+1));
        for(int i = 0; i <= n; i++){
            table[i][0] = true;
        }

        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= sum; j++){
                if(j-nums[i-1] < 0){
                    table[i][j] = table[i-1][j];
                }else{
                    table[i][j] = table[i-1][j] || table[i-1][j-nums[i-1]];
                }
            }
        }

        return table[n][sum];

    }
};

在这里插入图片描述


1:牛客-求正数数组的最小不可组成和

牛客
在这里插入图片描述

如果按照原生的背包问题可以这样理解:min为最轻物品的质量,sum为所有物品的总质量,假设有一个背包,其容量范围在[min,sum]之间,还有len件不同重量的物品、、、

也即把数组中的数据看作物品的重量,如果这些物品不能填满某个容量(范围为[min,max])的背包,就表示不能组成那个范围的数

class Solution {
public:
	/**
	 *	正数数组中的最小不可组成和
	 *	输入:正数数组arr
	 *	返回:正数数组中的最小不可组成和
	 */
	int getFirstUnFormedNum(vector<int> arr, int len) 
    {
        //范围为[min,sum];
        int sum=0,min=arr[0];
        int i,j;
        for(int i=0;i<len;i++)
        {
            sum+=arr[i];
            min=arr[i] < min ? arr[i] : min;
        }
        vector<int> dp(sum+1,0);
        for(i=0;i<len;i++)
        {
            for(j=sum;j>=arr[i];j--)//对于背包容量小于物品的直接忽略
            {
                if(dp[j] < dp[j-arr[i]]+arr[i])//选上了
                    dp[j]=dp[j-arr[i]]+arr[i];
                else//没选上
                    dp[j]=dp[j];
            }
        }
        
        //最后只要放入的重量不是那个区间的数肯定就是所求
        for(i=min;i<=sum;i++)
        {
            if(i!=dp[i])
                return i;
        }
        return sum+1;
    }
};

三:完全背包问题

(1)题目描述

  • LeetCode518:零钱兑换 II

给定不同面额的硬币 coins 和一个总金额 amount,写一个函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个

函数签名如下

int change(int amount, vector<int>& coins);

举个简单的例子,输入如下

amout = 5
coins = [1, 2, 5] 

算法返回4,因为共有如下4种方式可以凑出目标金额

5 = 5

5 = 2+2+1

5 = 2+1+1+1

5 = 1+1+1+1+1

(2)解题思路

此题可以转化为背包问题去做,等价描述为

有一个背包,最大容量为amout,有一系列物品coins,每个物品的重量为coins[i],物品数量无限,请问有多少种方法可以把背包恰好装满?

①:考虑状态和选择是什么

  • 状态:由于物品不断装入背包,所以状态有两个,分别为背包容量可选择的物品(每个物品可以重复选择)
  • 选择:对于每件物品,你的选择就是要么装进背包要么不装进背包(也就是0和1)

②:明确table数组定义:状态有两个,所以要定义成一个二维表。table[i][j]表示,对于前i个物品(可重复使用),当前背包的容量为j时,有table[i][j]种方法可以装满背包(即若只使用conis中的前i个硬币的面值,若要凑出金额j,有table[i][j]种方法)

  • 例如table[3][5] = 6,其含义为对于给定的一系列物品中,若只对前 3 个物品进行选择,当背包容量为 5 时,有6种方法可以装满背包
  • 最终返回table[N][amout],其中N为conis数组大小
  • 最简单情况table[0][....]=0(不使用任何硬币面值,自然无法凑出);table[...][0]=1(如果凑出的目标金额为0,那么唯一做法就是什么都不做)

③:根据选择,思考转移的逻辑:也即如何把选择用代码描述出来

  • 如果没有把第i个物品放在背包(也即不使用coins[i-1]这个面值的硬币):很显然,状态也不会变化,也即table[i][j]==table[i-1][j]
  • 如果把第i个物品放入了背包(也即使用了coins[i-1]这个面值的硬币):既然你决定用这个面值的硬币,那么接下来你就应该关注如何凑出金额j-coins[i-1]

(3)完整代码

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        int n  = coins.size();
        vector<vector<int>> table(n+1, vector<int>(amount+1));
        for(int i = 0; i <= n; i++){
            table[i][0] = 1;
        }

        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= amount; j++){
                if(j - coins[i-1] < 0){
                    table[i][j] = table[i-1][j];
                }else{
                    table[i][j] = table[i-1][j] + table[i][j-coins[i-1]];
                }
            }
        }
        return table[n][amount];
    }
};

在这里插入图片描述

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

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

相关文章

Java:Jar包反编译,解压和压缩

1、简述 JAR 文件就是 Java Archive &#xff08; Java 档案文件&#xff09;&#xff0c;它是 Java 的一种文档格式。 JAR 文件非常类似 ZIP 文件。准确的说&#xff0c;它就是 ZIP 文件&#xff0c;所以叫它文件包。JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中&a…

蓝桥杯必备算法分享——差分算法

AcWing—差分算法 文章目录AcWing---差分算法一、什么是差分&#xff1f;二、差分的作用三、一维差分模板四、二维差分五、二维差分构造方法图示&#xff1a;六、二维差分矩阵模板总结差分算法是前缀和算法的逆运算。两者可以对比着学习&#xff1a; 一、什么是差分&#xff1…

旭日图超越了传统的饼图和圆环图,能表达清晰的多层级和归属关系

“旭日图是什么&#xff1f;用来干什么&#xff1f;” “没听说过旭日图......” “旭日图不就是多层饼图嘛......” 鉴于很多人对旭日图都不太了解&#xff0c;那今天我们就用平台的旭日图为大家讲解。旭日图看似低调简单&#xff0c;却一点都不简单&#xff01; 旭日图由多…

关于pool.apply_async的学习【参数问题】

一、学习 参考&#xff1a;(1条消息) python pool.apply_async调用 参数为dataset的函数 不执行问题解决_嗯嗲和滴的博客-CSDN博客_pool.apply_async参数 一个参数的情况 一定要加逗号 在只有一个参数要传时 需要写成列表/元组的形式&#xff1a; task_fun.apply_async(args[v…

从零开始学前端:垃圾回收机制,闭包,案例 --- 今天你学习了吗?(JS:Day11)

从零开始学前端&#xff1a;程序猿小白也可以完全掌握&#xff01;—今天你学习了吗&#xff1f;&#xff08;JS&#xff09; 复习&#xff1a;从零开始学前端&#xff1a;作用域、执行顺序 — 今天你学习了吗&#xff1f;&#xff08;JS&#xff1a;Day10&#xff09; 文章目…

网络编程——封装和分用(图解)

一、什么是封装 &#x1f48c;&#x1f48c;&#x1f48c;网络编程中的封装&#xff0c;并不是Java面向对象思想里的封装&#xff0c;继承&#xff0c;多态的封装.它是应用程序通过TCP协议传送数据时&#xff0c;每一次进行包装送入网络中&#xff0c;像极了发快递时的你. 思考…

FFplay文档解读-51-多媒体资源

33. 多媒体资源 以下是当前可用多媒体源的说明。 33.1 amovie 这与电影源相同&#xff0c;但默认情况下会选择音频流。 33.2 movie 从电影容器中读取音频和/或视频流。 它接受以下参数&#xff1a; filename要读取的资源的名称&#xff08;不一定是文件;它也可以是通过某…

[附源码]java毕业设计基于web的健康信息管理系统

项目运行 环境配置&#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…

Oracle Primavera Unifier文档管理器(Document Manager)

目录 引言 介绍 引言 在 Oracle Primavera Unifier 中&#xff0c;文档管理器维护项目的所有文件和文档。每个项目/外壳都包含自己的文档管理器&#xff0c;另一个文档管理器驻留在公司级别。管理员确保员工和项目/shell 成员始终使用最新版本的文档&#xff0c;并使访问和分…

热成像仪在LED产品的应用

热成像仪对LED产品的检测 LED产品检测 LED作为取代传统照明工具(如白炽灯、卤素灯等)的新型光源&#xff0c;但其散热效果严重影响LED的实际使用寿命&#xff0c;散热工艺成为LED应用和发展的关键因素&#xff0c;红外热像仪可以进行LED温度检测&#xff0c;帮助验证散热工艺。…

如何对珍贵水生物标本提供三维重建,数字化技术助力长江大保护...

近日&#xff0c;由武汉白鱀豚保护基金会发起的“看见长江的美好”系列长江大保护行动中&#xff0c;英特尔 Evo X BMW i 打造了“移动数字探索工作室”&#xff0c;携手【云端地球】团队来到了拥有百年生命印记的地方&#xff0c;这里收藏着包括白鲟、白鱀豚、中华鲟、长江江豚…

密码在智能汽车数据安全领域的应用研究报告

开放隐私计算 开放隐私计算 开放隐私计算OpenMPC是国内第一个且影响力最大的隐私计算开放社区。社区秉承开放共享的精神&#xff0c;专注于隐私计算行业的研究与布道。社区致力于隐私计算技术的传播&#xff0c;愿成为中国 “隐私计算最后一公里的服务区”。 177篇原创内容 …

HTTP状态码301和302的区别

简介 在HTTP状态码中&#xff0c;3XX表示重定向&#xff0c;指网页发生了转移&#xff0c;重定向到目标地址中。 301&#xff1a;表示永久性转移 &#xff08;Permanently Moved&#xff09; 302&#xff1a;表示临时性转移&#xff08;Temporarily Moved&#xff09; 说明…

java基础新

目录 集合总结 ​编辑Java异常体系 常见的运行时异常&#xff1f; Object类有哪些方法 ​编辑权限修饰符 分布式锁 Redis和Zookeeper实现分布式所哪个效率高 分布式事务 事务失效的8种原因 TCC 如果出现网络连不通怎么办&#xff1f; CAP理论 CAP有哪些组合方式&…

3环境变量

文章目录前言1. 概念引入2. 环境变量PATH3. 本地环境变量与系统环境变量4. set 和 env5. main函数带参数前言 大家一开始学习Linux时候,是否对其各种命令感到过好奇,为何这样输入不同就会执行不同,他的底层实现逻辑到底是怎么样的呢? 在解答这个疑惑之前,我们需要学习环境变…

计算机网络-网络层(移动IP通信过程,网络层设备路由器,路由表与路由转发)

文章目录1. 移动IP2. 网络层设备&#xff08;路由器&#xff09;1. 移动IP 移动IP技术是移动结点(计算机/服务器等)以固定的网络IP地址&#xff0c;实现跨越不同网段的漫游功能. 并保证了基于网络lP的网络权限在漫游过程中不发生任何改变。 重点知识点&#xff1a; 移动结点…

互联网食堂大比拼,谁才是互联网养猪场?

虽然疫情导致经济不景气&#xff0c;但是互联网行业怎么会如此不禁波折呢&#xff1f; 果不其然&#xff0c;疫情下的大厂还是大厂&#xff01; 互联网大厂的福利多多&#xff0c;其中最容易让人忽视&#xff0c;但也最让人羡慕的就是互联网大厂的食堂了。 民以食为天&#xff…

线粒体 ClpP 介导的蛋白水解作用可选择性诱导癌细胞死亡

图1. 本篇文章的几大亮点 首先&#xff0c;研究人员利用基因工程的方法&#xff0c;在人 ClpP 上建造了一个有持续活性地点突变&#xff08;Y118A&#xff09;。基于 OCL-AML3 和 Z138 肿瘤细胞系&#xff0c;研究人员研究 Y118A 点突变对于肿瘤细胞的影响。研究发现&#xff…

耗时半月,终于把牛客网上的Java面试八股文整理成了PDF合集

前段时间有不少小伙伴们私信我&#xff0c;最近的面试题越来越困难了&#xff0c;要背的八股文也越来越多了&#xff0c;考核的也越来越细腻了&#xff0c;摆明了就是想让我们面试造航母嘛&#xff0c;真的是太为难我们这些程序员了。。。 这不&#xff0c;为了帮大家节约时间…

如何对低代码/无代码平台进行分类?

如何对低代码/无代码平台进行分类&#xff1f;一个例子就能讲清楚&#xff01; 周末你外出露营&#xff0c;在野外需要搭一个帐篷。有两种方法&#xff1a; 一种是最原始的搭帐篷方法&#xff0c;即有隔水布、外账、内账、营柱骨架等等......另一种是直接“封装好”的&#x…