01背包之---应用篇

news/2025/2/26 1:51:07

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、01背包之---背包是否能被装满?
    • 例题1.
    • 分析题意
    • 例题2.
    • 分析题意
  • 二、01背包之---装满背包有多少种组合?
    • 例题1.
    • 分析题意
  • 三、01背包之---容量为N的背包最多可以装多少物品?
    • 例题1.
    • 分析题意
  • 总结


前言

上文我们讲完了01背包的一维的写法,了解了01背包问题的模型,就是每个物品可以被拿一次,求在有限的背包容量下,所能取得的最大价值是多少,但在刷题过程中题目往往不是直接给你一个这样的模板然后让你去求,所以我们今天来讲三种明面上不是背包问题的题目但其实就是01背包的题目
下面例题解答我们统一用一维dp解答,不仅代码比二维简洁,而且复杂度更低


一、01背包之—背包是否能被装满?

例题1.

在这里插入图片描述

分析题意

首先这道题要我们把一个数组分成两个数组,两个数组的和相同
首先这两个数组的各自总和ans肯定为总数组的和sum/2
那么如果这个原数组的和sum为奇数,肯定就不能分成功,这个我们现在可以单独判出来

其次我们仔细想一下,我们现在已知什么? 我们知道原数组总和sum
那是不是也知道了要分成两个数组,每个数组的和为 sum/2
是不是问题就转换为求背包容量为sum/2的一个背包,从原数组中选商品,看能不能正好装满这个背包
我们直接求背包容量为sum/2所能装的最大价值即可,如果装的最大价值就等于sum/2,那意思是不是就是可以装满这个背包,从而可以成功分为两个数组呢?

那么接下来我们直接上代码

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum=0;
        for(int i=0;i<nums.size();i++)sum+=nums[i];//求数组总和
        int dp[10001];
        //初始化
        for(int i=0;i<10001;i++)dp[i]=0;
        if(sum%2==1)return false;
        int target=sum/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]);
            }
        }
        if(dp[target]==target)return true;
        return false;
    }
};

在这里插入图片描述
关于dp数组的初始化以及遍历问题,请回顾上篇文章,这里不再过多讲解

例题2.

在这里插入图片描述

分析题意

这道题其实更第一题很类似
大家可以发现,其实也是分成两个数组,然后使两个数组的差最小,大家仔细想想,是不是这个道理?
我们直接上代码

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum=0;
        for(int i=0;i<stones.size();i++)sum+=stones[i];//计算总和
        int dp[10001];
        for(int i=0;i<10001;i++)dp[i]=0;//初始化
        int target=sum/2;
        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]);
            }
        }
        if(2*dp[target]==sum)return 0;
        return sum-2*dp[target];
    }
};

在这里插入图片描述

二、01背包之—装满背包有多少种组合?

例题1.

在这里插入图片描述

分析题意

刚看到题目的时候我也一脸懵逼,后来发现,这就是排列组合的题目,我们可以把原数组分成两块,一方存储加号的元素,一方存储减号的元素,我们只需要找到有多少种组合的和满足与其中一方的和就行了,我们这里以加号的元素为准

假设所有+号的元素的总和为x 所有-号的元素的总和为y 原数组总和为sum 目标值为target
那么 x+y=sum
x-y=target

所以 x=(target+sum)/2
所以我们只需要找到装满容量为x的背包 有多少种组合即可
但这里有个注意点,这个 (target+sum)/2一定可以被整除嘛??
答案是一定的,不然凑不出结果,大家可以在草稿纸上写一下

所以我们现在有了dp数组
dp[j]表示容量为j的背包 装满后有多少种组合

那么递推公式呢?
dp[j]+=dp[j-nums[i]]
为什么呢?
大家可以想一下
dp[5]一定由dp[1]+dp[2]+dp[3]+dp[4]相加而来,那么为什么呢
比如我此时dp[1]=1,表示装满容量为1的背包有1种方式,那么此时我如果再装一个4的物品,是不是直接满容量了,所以dp[5]可以从dp[1]得来,同理可得dp[5]=dp[1]+dp[2]+dp[3]+dp[4]

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum=0;
        for(int i=0;i<nums.size();i++)sum+=nums[i];//计算总和
        int jiafa=(target+sum)/2;
        if(abs(target)>sum||(target+sum)%2==1)return 0;//无法构造出
       //装满容量为i的背包有dp[i]种组合
        int dp[10001];
        for(int i=1;i<10001;i++)dp[i]=0;
        dp[0]=1;
        for(int i=0;i<nums.size();i++)
        {
            for(int j=jiafa;j>=nums[i];j--)
            {
                dp[j]+=dp[j-nums[i]];
            }
        }
        return dp[jiafa];
    }
};

三、01背包之—容量为N的背包最多可以装多少物品?

例题1.

在这里插入图片描述

分析题意

我们仔细分析题目,我们可以把m个0 n个1当成背包容量,大家想一想是不是这样
我们最初一开始讲的01背包问题的时候,只有一个背包容量,然后求在这个容量的情况下,所能装的最大物品价值为多少,当时我们写的是一维的dp[j] 只有一个容量j 那这里我们是不是相当于有两个容量 m和ndp[m][n]表示在有m个0和n个1的情况下 所能装的最多的字符串个数为dp[m][n]个

此时我们注意!!!我们这虽然开的是二维的dp[m][n],但这两个变量都代表背包容量,相当于01背包中的dp[j],如果讲01背包问题写成二维的dp[i][j],那么我们这里就要写成dp[i][m][n]三维表示,因为i表示的是物品,后面两个都是背包容量,大家不要搞混

一维的递推公式维dp[j]=max(dp[j],dp[j-weight[i]]+value[i])
其实我们通过对比很快就能发现此题的递推公式为 dp[m][n]=max(dp[m][n],dp[m-weight[i]][n-weight[i]]+1) 这两个的意思都是要么不拿此物品 要么拿

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        int dp[m+5][n+5];//容量为m和n的背包能装多少个物品
        for(int i=0;i<m+2;i++)
        {
            for(int j=0;j<n+2;j++)
            {
                dp[i][j]=0;
            }
        }
        dp[0][0]=0;
        //求dp[m][n];//m个0 n个1
        for(int i=0;i<strs.size();i++)//遍历物品
        {
            int x=0;//记录有多少个0
            int y=0;//记录有多少个y
            for(int k=0;k<strs[i].size();k++)
            {
                if(strs[i][k]=='0')x++;
                else
                {
                    y++;
                }
            }
            //遍历背包
            for(int j1=m;j1>=x;j1--)
            {
                for(int j2=n;j2>=y;j2--)
                {
                    dp[j1][j2]=max(dp[j1][j2],dp[j1-x][j2-y]+1);
                }
            }
        }
        return dp[m][n];

    }
};

在这里插入图片描述

总结

今天讨论了01背包问题的背包是否能被装满装满背包有多少种组合的问题容量为N的背包最多可以装多少物品我们都是用一维来写,这样复杂度更低,判断背包是否能被装满,直接判断背包最大容量时可以装多少价值即可,如果和背包容量相等,就是装满了,在讨论装满背包有多少种组合的问题的时候,我们用递推公式 dp[j]+=dp[j-nums[i]] 即可
当判断一定容量的背包能装多少个的时候,在dp递推的时候里面把加value[i]改成+1即可,因为要求的不是最大价值,而是最多的数量


http://www.niftyadmin.cn/n/5867052.html

相关文章

C++和OpenGL实现3D游戏编程【连载23】——几何着色器和法线可视化

欢迎来到zhooyu的C++和OpenGL游戏专栏,专栏连载的所有精彩内容目录详见下边链接: 🔥C++和OpenGL实现3D游戏编程【总览】 1、本节实现的内容 上一节课,我们在Blend软件中导出经纬球模型时,遇到了经纬球法线导致我们在游戏中模型光照显示问题,我们在Blender软件中可以通过…

Win11在docker环境安装homeassistant,安装HACS并集成小米官方组件

目标是在docker中安装homeassistant&#xff0c;并且将本地目录做为工作目录&#xff0c;方便管理。然后在ha中安装HACS商店&#xff0c;并集成小米官方组件。 拉取ha镜像 首先在docker中配置好加速条件&#xff08;docker引擎使用阿里云&#xff0c;并挂梯子&#xff09;&…

HybridCLR+Adressable+Springboot热更

本文章会手把手教大家如何搭建HybridCLRAdressableSpringboot热更。 创作不易&#xff0c;动动发财的小手点个赞。 安装华佗 首先我们按照官网的快速上手指南搭建一个简易的项目&#xff1a; 快速上手 | HybridCLR 注意在热更的代码里添加程序集。把用到的工具放到程序集里…

本地部署DeepSeek-R1(Ollama+Docker+OpenWebUI知识库)

安装Ollama 打开 Ollama官网 https://ollama.com/下载安装 Ollama服务默认只允许本机访问&#xff0c;修改允许其它主机访问 OLLAMA_HOST0.0.0.0 ollama serve也可以添加系统环境变量 都知道模型体积很大&#xff0c;顺便也通过环境变量修改模型存放位置&#xff0c;我这…

最长递增子序列(贪心算法)思路+源码

文章目录 题目[](https://leetcode.cn/problems/longest-increasing-subsequence/)算法原理源码总结题目 首先,要掌握动态规划加二分查找 算法原理 1.回顾dp的解法 状态表示:dp[i]表示:以i位置的元素为结尾的所有的子序列中,最长递增子序列的长度 状态转移方程:dp[i]= m…

2. MySQL的数据目录(详解讲解)

2. MySQL的数据目录(详解讲解) 文章目录 2. MySQL的数据目录(详解讲解)1. MySQL8 的主要目录结构1.1 相关命令目录1.2 配置文件目录 2. 数据库和文件系统的关系2.1 查看默认数据库2.2 数据库在文件系统中的表示 3. 表在文件系统中的表示3.1 InnoDB存储引擎模式3.2 MyISAM存储引…

【HeadFirst系列之HeadFirstJava】第5天之超强力方法 —— 从战舰游戏到循环控制

编写程序&#xff1a;超强力方法 —— 从战舰游戏到循环控制 在《Head First Java》的第五章节中&#xff0c;作者通过一个简单的战舰游戏示例&#xff0c;深入讲解了如何编写Java程序&#xff0c;并重点介绍了方法和循环控制的使用。这一章节的核心思想是&#xff1a;通过模块…

MySQL 最左前缀原则:原理、应用与优化

目录 引言 什么是复合索引&#xff1f; 什么是最左前缀原则&#xff1f; 示例 最左前缀原则的原理 最左前缀原则的应用场景 1. 等值查询 2. 范围查询 3. 部分列查询 4. 排序和分组 最左前缀原则的优化技巧 1. 合理设计复合索引 2. 避免跳过索引列 3. 覆盖索引 4.…