DP:01背包问题

一、背包问题的概述

背包问题是⼀种组合优化的NP完全问题。
本质上是为了找出“带有限制条件的组合最优解”

1、根据物品的个数,分为如下几类:

• 01背包问题:每个物品只有⼀个(重点掌握)
• 完全背包问题:每个物品有无限多个(重点掌握)

• 多重背包问题:每件物品最多有n个
• 混合背包问题:每个物品会有上⾯三种情况
• 分组背包问题:物品有n组,每组物品⾥有若⼲个,每组⾥最多选⼀个物品

2、根据背包是否装满,⼜分为两类

• 不⼀定装满背包(重点)
• 背包⼀定装满(重点)

3、优化方案

• 空间优化:滚动数组(重点掌握)
• 单调队列优化
• 贪心优化

4、根据限定条件的个数,⼜分为两类

• 限定条件只有⼀个:比如体积->普通的背包问题(重点)
• 限定条件有两个:比如体积+重量->⼆维费用背包问题(重点)

5、根据不同的问法,⼜分为很多类:

• 输出方案
• 求方案总数
• 最优方案
• 方案可行性

        背包问题的题型非常多样,其中最重要以及基础的就是01背包和完全背包以及背包是否装满的讨论(会通过牛客的两道模版题探究),还有滚动数组的优化策略( 在以往的动态规划中,我们几乎很少去谈论空间优化,因为对于一道dp题来说,能解决出来就已经很不容易了,我们不太会关注其空间复杂度。但是在背包问题中,滚动数组的优化是有一定套路可言的,并且在某些情况下对时间也是有一定优化的!!

二、01背包[模版]

【模板】01背包_牛客题霸_牛客网

#include<iostream>
#include<string.h>
using namespace std;
//定义成全局,就不用在栈里面进行初始化,并且我们可以在栈上开辟的空间更大const int N=1001;
int n,V,v[N],w[N];
int dp[N][N];int main() 
{cin>>n>>V;//个数和体积for(int i=1;i<=n;++i) cin>>v[i]>>w[i];//解决第一问for(int i=1;i<=n;++i)for(int j=1;j<=V;++j){dp[i][j]=dp[i-1][j];//不选第i个物品的情况if(j>=v[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);}   cout<<dp[n][V]<<endl;//解决第二问memset(dp,0,sizeof dp);//修改成0//先进行初始化for(int j=1;j<=V;++j) dp[0][j]=-1;//跟0区分开for(int i=1;i<=n;++i)for(int j=1;j<=V;++j){dp[i][j]=dp[i-1][j];//不选第i个物品的情况if(j>=v[i]&&dp[i-1][j-v[i]]!=-1) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);}   cout<<(dp[n][V]==-1?0:dp[n][V])<<endl;
}

滚动数组优化(空间复杂度N^2——>N   时间复杂度常数提升

#include<iostream>
#include<string.h>
using namespace std;
//定义成全局,就不用在栈里面进行初始化,并且我们可以在栈上开辟的空间更大
const int N=1001;
int n,V,v[N],w[N];
int dp[N][N];int main() 
{cin>>n>>V;//个数和体积for(int i=1;i<=n;++i) cin>>v[i]>>w[i];//解决第一问for(int i=1;i<=n;++i)for(int j=V;j>=v[i];--j)dp[j]=max(dp[j],dp[j-v[i]]+w[i]);cout<<dp[V]<<endl;//解决第二问memset(dp,0,sizeof dp);//修改成0//先进行初始化for(int j=1;j<=V;++j) dp[j]=-0x3f3f3f3f;//跟0区分开for(int i=1;i<=n;++i)for(int j=V;j>=v[i];--j)dp[j]=max(dp[j],dp[j-v[i]]+w[i]);cout<<(dp[V]<0?0:dp[V])<<endl;
}

       对于不存在的状态,因为我们该题中要求的是max,所以我们设成-0x3f3f3f3f保证该状态不被选到,设置成这个的原因是避免了越界的风险同时又保证了不存在的状态是小于0的,且不会影响填报!!

三、和为目标和的最长子序列长度

. - 力扣(LeetCode)

       这题就是非常明显的01背包问题,其中每个数只有选或者不选,目标值相当于是容量,且要刚刚好。 dp[i][j]表示从前i个数选,和恰好为j的最长子序列。

class Solution {
public:int lengthOfLongestSubsequence(vector<int>& nums, int target) {int n=nums.size();//01背包问题  dp[i][j]表示从前i个数选择 正好凑成j的的子序列的最长长度vector<vector<int>> dp(n+1,vector<int>(target+1));//分析状态转移方程 dp[i][j] //如果我不选i dp[i-1][j]//如果我选i   dp[i-1][j-nums[i-1]]+1 //初始化 如果i为0无数可选  没有这个状态for(int j=1;j<=target;++j) dp[0][j]=-0x3f3f3f3f;//给一个小的值  保证选最大值的时不会被选上for(int i=1;i<=n;++i)for(int j=0;j<=target;++j){dp[i][j]=dp[i-1][j];if(j>=nums[i-1]) dp[i][j]=max(dp[i][j],dp[i-1][j-nums[i-1]]+1);}return dp[n][target]<0?-1:dp[n][target];}
};

滚动数组优化:

class Solution {
public:int lengthOfLongestSubsequence(vector<int>& nums, int target) {int n=nums.size();//01背包问题  dp[i][j]表示从前i个数选择 正好凑成j的的子序列的最长长度vector<int> dp(target+1,-0x3f3f3f3f);//分析状态转移方程 dp[i][j] //如果我不选i dp[i-1][j]//如果我选i   dp[i-1][j-nums[i-1]]+1 //初始化 如果i为0无数可选  没有这个状态dp[0]=0;for(int i=1;i<=n;++i)for(int j=target;j>=nums[i-1];--j)dp[j]=max(dp[j],dp[j-nums[i-1]]+1);return dp[target]<0?-1:dp[target];}
};

四、分割等和子集(需转化)

. - 力扣(LeetCode)

该题并不能直接用01背包问题,首先需要先将问题进行转化——在数组中选一些数,让这些数的和为sum/2。 

class Solution {
public:bool canPartition(vector<int>& nums) {int sum=accumulate(nums.begin(),nums.end(),0);if(sum%2) return false;//是奇数,直接返回//是偶数的时候 dp[i][j]表示从前i个数中选,所有选法中能否凑成j这个数int aim=sum/2;int n=nums.size();vector<vector<bool>> dp(n+1,vector<bool>(aim+1));//初始化,当j=0时,显然都是true  当i=0时,必然为falsefor(int i=0;i<=n;++i) dp[i][0]=true;//开始填表for(int i=1;i<=n;++i)for(int j=1;j<=aim;++j)//不选i的话  dp[i][j]=dp[i-1][j]//选i的话    dp[i][j]=dp[i-1][j-nums[i-1]]   前提j>=nums[i-1]{dp[i][j]=dp[i-1][j];if(j>=nums[i-1]) dp[i][j]=dp[i][j]||dp[i-1][j-nums[i-1]];}return dp[n][aim];}
};

滚动数组优化:

class Solution {
public:bool canPartition(vector<int>& nums) {int sum=accumulate(nums.begin(),nums.end(),0);if(sum%2) return false;//是奇数,直接返回//是偶数的时候 dp[i][j]表示从前i个数中选,所有选法中能否凑成j这个数int aim=sum/2;int n=nums.size();vector<bool> dp(aim+1);//初始化,当j=0时,显然都是true  当i=0时,必然为falsedp[0]=true;//开始填表for(int i=1;i<=n;++i)for(int j=aim;j>=nums[i-1];--j)//不选i的话  dp[i][j]=dp[i-1][j]//选i的话    dp[i][j]=dp[i-1][j-nums[i-1]]   前提j>=nums[i-1]dp[j]=dp[j]||dp[j-nums[i-1]];return dp[aim];}
};

 五、目标和(需转化)

. - 力扣(LeetCode)

class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {// 从nums中选择一些数能够凑成sum+target/2  转化成01背包问题int sum=accumulate(nums.begin(),nums.end(),0);int aim=(sum+target)>>1;if(aim<0||(sum+target)%2) return 0;int n=nums.size();//dp[i][j] 从前i个数选 变成j有多少种选法    //如果不选i dp[i-1][j]//如果选i   +=dp[i-1][j-nums[i-1]]//分析初始化 i=0的时候 必为0  j=0的时候 不好判断,因为nums[i]可能是0 //但是不需要初始化,因为要满足j>=nums[i] 那么nums[i]必然要为0才可以满足//所以绝对不会用到斜对角的值,而是只会用到上面的状态。vector<vector<int>> dp(n+1,vector<int>(aim+1));dp[0][0]=1;for(int i=1;i<=n;++i)for(int j=0;j<=aim;++j) {dp[i][j]=dp[i-1][j];if(j>=nums[i-1]) dp[i][j]+=dp[i-1][j-nums[i-1]];}return dp[n][aim];}
};

 滚动数组优化:

class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {// 从nums中选择一些数能够凑成sum+target/2  转化成01背包问题int sum=accumulate(nums.begin(),nums.end(),0);int aim=(sum+target)>>1;if(aim<0||(sum+target)%2) return 0;int n=nums.size();//dp[i][j] 从前i个数选 变成j有多少种选法    //如果不选i dp[i-1][j]//如果选i   +=dp[i-1][j-nums[i-1]]//分析初始化 i=0的时候 必为0  j=0的时候 不好判断,因为nums[i]可能是0 //但是不需要初始化,因为要满足j>=nums[i] 那么nums[i]必然要为0才可以满足//所以绝对不会用到斜对角的值,而是只会用到上面的状态。vector<int> dp(aim+1);dp[0]=1;for(int i=1;i<=n;++i)for(int j=aim;j>=nums[i-1];--j) dp[j]+=dp[j-nums[i-1]];return dp[aim];}
};

六、最后一块石头的重量||(需转化)

. - 力扣(LeetCode)

class Solution {
public:int lastStoneWeightII(vector<int>& nums) {//让一堆里面的数尽可能接近sum/2int sum=accumulate(nums.begin(),nums.end(),0);int aim=sum/2,n=nums.size();//dp[i][j]表示从前i个数选择,总和不超过j,此时所有元素的最大和vector<vector<int>> dp(n+1,vector<int>(aim+1));//分析初始化 如果都为0 就返回0 如果i为0 也是0  如果j为0 不用初始化for(int i=1;i<=n;++i)for(int j=1;j<=aim;++j){//如果不选i dp[i-1][j]//如果选i  dp[i-1][j-nums[i-1]] 找最大和dp[i][j]=dp[i-1][j];if(j>=nums[i-1]) dp[i][j]=max(dp[i][j],dp[i-1][j-nums[i-1]]+nums[i-1]);}return sum-2*dp[n][aim];}
};

滚动数组优化:

class Solution {
public:int lastStoneWeightII(vector<int>& nums) {//让一堆里面的数尽可能接近sum/2int sum=accumulate(nums.begin(),nums.end(),0);int aim=sum/2,n=nums.size();//dp[i][j]表示从前i个数选择,总和不超过j,此时所有元素的最大和vector<int> dp(aim+1);//分析初始化 如果都为0 就返回0 如果i为0 也是0  如果j为0 不用初始化//如果不选i dp[i-1][j]//如果选i  dp[i-1][j-nums[i-1]] 找最大和for(int i=1;i<=n;++i)for(int j=aim;j>=nums[i-1];--j)dp[j]=max(dp[j],dp[j-nums[i-1]]+nums[i-1]);return sum-2*dp[aim];}
};

七、将一个数字表示成幂的和的方案数

. - 力扣(LeetCode)

知识点1:double不支持取模,需要取模又担心溢出只能使用long long

知识点2:pow函数是求数的任意次幂

知识点3:10^9+7相当于1e9+7

class Solution {
public:int numberOfWays(int n, int x) {//统计方案数//dp[i][j]表示从前i个数的x次幂之和  恰好等于j 的方案数//i=0时 无数可选 方案肯定是const int N=1e9+7;vector<vector<long long>> dp(n+1,vector<long long>(n+1)); //double不支持取模    dp[0][0]=1;for(int i=1;i<=n;++i)for(int j=0;j<=n;++j){//不选i dp[i][j]=dp[i-1][j]//选i   dp[i][j]+=dp[i-1][j-pow(i,x)]dp[i][j]=dp[i-1][j];long long p=pow(i,x); if(j>=p) dp[i][j]+=dp[i-1][j-p];dp[i][j]%=N;}return dp[n][n];}
};

 滚动数组优化:

class Solution {
public:int numberOfWays(int n, int x) {//统计方案数//dp[i][j]表示从前i个数的x次幂之和  恰好等于j 的方案数//i=0时 无数可选 方案肯定是const int N=1e9+7;vector<long long> dp(n+1); //double不支持取模    dp[0]=1;for(int i=1;i<=n;++i){long long p=pow(i,x);for(int j=n;j>=p;--j)//不选i dp[i][j]=dp[i-1][j]//选i   dp[i][j]+=dp[i-1][j-pow(i,x)]dp[j]=(dp[j]+dp[j-p])%N;}return dp[n];}
};

 

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

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

相关文章

ffmpeg封装和解封装介绍-(10)综合完成视频重编码为h265,解封装解码编码再封装

主函数逐句解析&#xff1a; 由于代码太多我们只解析主函数&#xff0c;&#xff08;其他封装函数见前面文章&#xff0c;同时用到了解码编码封装代码&#xff09;。 初始化和参数处理 int main(int argc, char* argv[]) {/// 输入参数处理string useage "124_test_x…

【计算机网络】已解决:“‘ping‘ 不是内部或外部命令,也不是可运行的程序或批处理文件”报错

文章目录 一、问题分析背景二、可能出错的原因三、错误代码示例四、正确解决方法与示例五、注意事项 已解决“‘ping’ 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件”报错 一、问题分析背景 在Windows操作系统中&#xff0c;ping 命令是一个常用的网络诊断…

线程池ThreadPoolExecutor使用指南

线程池ThreadPoolExecutor使用指南 &#x1f9d0;使用线程池的好处是什么&#xff1f; 统一管理&#xff0c;减少资源获取创建的开销&#xff0c;提高利用率。 &#x1f527;线程池的参数 ​ThreadPoolExecutor​ 3 个最重要的参数&#xff1a; ​corePoolSize​ : 任务队列…

docker login 报错: http: server gave HTTP response to HTTPS client

环境&#xff1a; 自建 Harbor、Docker 1. 问题分析 # 命令&#xff0c;这里用的是 IP&#xff0c;可以为域名 docker login -u test 172.16.51.182:31120 # 输入密码 Password:# 报错如下&#xff1a; Error response from daemon: Get "https://172.16.51.182:31120/…

[Algorithm][贪心][增减字符串匹配][分发饼干][最优除法][跳跃游戏Ⅱ][跳跃游戏]详细讲解

目录 1.增减字符串匹配1.题目链接2.算法原理详解3.代码实现 2.分发饼干1.题目链接2.算法原理详解3.代码实现 3.最优除法1.题目链接2.算法原理详解3.代码实现 4.跳跃游戏 II1.题目链接2.算法原理详解3.代码实现 5.跳跃游戏1.题目链接2.算法原理详解3.代码实现 1.增减字符串匹配 …

期末复习6--链表头插法(逆序)尾插法(顺序)---输出链表

头插法 #include <stdio.h> #include <stdlib.h>struct Node //定义结构体 {char data; //数据域struct Node * next; //指针域 };/* 请在这里填写答案 */void PrintList (struct Node * head) {struct Node * s;if(head NULL){printf("None&qu…

Apipost模拟HTTP客户端

模拟HTTP客户端的软件有很多&#xff0c;其中比较著名的就有API-FOX、POSTMAN。 相信很多小伙伴都使用POSTMAN。这篇博客主要介绍Apipost的原因是&#xff0c;Apipost无需下载&#xff0c;具有网页版。 APIFOX的站内下载&#xff1a; Api-Fox&#xff0c;类似于PostMan的软件…

JavaFX 节点

JavaFX Node类javafx.scene.Node是添加到JavaFX 场景图的所有组件 的基类&#xff08;超类&#xff09; 。JavaFX Node 类是抽象的&#xff0c;因此你只需将 Node 类的子类添加到场景图中。场景图中的所有 JavaFX Node 实例共享一组由 JavaFX Node 类定义的公共属性。本 JavaFX…

一文了解Redis

一.什么是Redis 与MySQL一样&#xff0c;Redis也是客户端服务器结构的程序&#xff0c;是基于内存的键值对存储系统&#xff0c;属于NoSQL的一种。与很多键值对数据库不同的是&#xff0c;Redis 中的值可以是由 string&#xff08;字符串&#xff09;、hash&#xff08;哈希&a…

【算法专题--链表】删除排序链表中的重复元素II -- 高频面试题(图文详解,小白一看就懂!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐ 双指针 -- 采用 哨兵位头节点 &#x1f95d; 什么是哨兵位头节点&#xff1f; &#x1f34d; 解题思路 &#x1f34d; 案例图解 四、总结与提炼 五、共勉 一、前言 删除排序链表中的重复元素II元素这道题&#xff0c…

【JKI SMO】框架讲解(二)

JKI State Machine 讲解 将JKI State Machine 模板拖曳到程序框图中&#xff0c; 如下图&#xff0c; 此模板会默认放置一个OK按钮在前面板中&#xff0c;用于提示用户如何增加一个简单的用户事件去使用此框架。 “Event Structure”&#xff0c;Idle&#xff1a;此分支可以设…

【JS重点17】原型链(面试重点)

一&#xff1a;原型链底层原理 以下面一段代码为例&#xff0c;基于原型对象&#xff08;Star构造函数的原型对象&#xff09;的继承使得不同构造函数的原型对象关联在一起&#xff08;此处是最大的构造函数Object原型对象&#xff09;&#xff0c;并且这种关联的关系是一种链…

C#联合Halcon机器视觉框架源码—升级版

相较于之前的NxtVision&#xff0c;本软件代码架构更加合理&#xff0c;且新增ui设计器、原来的vb脚本改为C#脚本&#xff0c;并尝试将视觉与运动控制相结合&#xff0c;是一体化的框架。 对源码有需求的&#xff0c;订阅本专栏后&#xff0c;私信我领取。

活动集锦 | 英码科技积极参与行业盛会,AI赋能城市数字化转型

在当今数字经济时代&#xff0c;城市全域数字化转型已经成为提升城市管理效能、优化资源配置、推动经济发展的重要手段。英码科技始终致力于为企业打造高效、低成本的行业应用方案&#xff0c;助力企业实现数字化转型。近日&#xff0c;英码科技受邀参加了多场行业展示活动&…

操作系统复习-线程同步

互斥量 两个线程的指令交叉执行互斥量可以保证先后执行称为原子性 原子性是指一系列操作不可被中断的特性这一系列操作要么全部执行完成&#xff0c;要么全部没有执行不存在部分执行部分未执行的情况 互斥锁 互斥量是最简单的线程同步的方法互斥锁&#xff0c;处于两态之一的…

01 飞行器设计 —— 一门独立的学科

01 飞行器设计 —— 一门独立的学科 01 引言02 飞机设计概述2-1 什么是飞机设计&#xff1f;2-1 飞机设计是从哪里开始的&#xff1f;2-2 如何成为一名飞机设计师&#xff1f;2-4 本书的组织 参考文献 说明&#xff1a;关于Raymer的《Aircraft Design》的读书笔记&#xff1b; …

解读ROS功能包模块的步骤

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言解读ROS功能包模块的步骤前言 认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长! 推荐开发经验及方法博客专栏: [https:/…

哇塞,超好吃的麻辣片,一口就爱上

最近&#xff0c;我发现了一款让人欲罢不能的美食——食家巷麻辣片&#xff01;&#x1f60d; 一打开包装&#xff0c;那浓郁的麻辣香气就扑鼻而来&#xff0c;瞬间刺激着我的嗅觉神经。&#x1f603;食家巷麻辣片的外观色泽鲜艳&#xff0c;红通通的一片&#xff0c;看着就特…

Android断点续传原理及实现

常见两种网络请求方式 一、 HttpURLConnection HttpURLConnection的setRequestProperty()方法&#xff0c;对我们要读取的字节部分进行控制&#xff0c;比如: 1.Range0-100代表只读取前100个字节。 2.Range100-500代表读取从第100个字节开始&#xff0c;读到第500个字节为止。…

常见的宽基指数基金

指数基金投资指南 ❝ 这篇博客里面的内容主要来自于银行螺丝钉的《定投十年&#xff0c;财务自由》和《指数基金投资指南》这两本书中章“常见的宽基指数”&#xff0c;最近第三次读这本书&#xff0c;打算做一点笔记加深自己的印象。 博客中很多内容是从书中摘抄的&#xff0c…