背包初步(0-1背包、完全背包)

当月光洒在我的脸上
我想我就快变了模样
有一种叫做撕心裂肺的汤
喝了它有神奇的力量

动态规划初步(完全背包)

目录

  • 动态规划初步(完全背包)
    • 0-1背包简介
    • 完全背包
      • 检查数组是否存在有效划分(前缀划分DP)
      • 单词拆分(求排列数)
      • 疯狂的采药(求组合数)
      • 自然数拆分

0-1背包简介

在「背包 dp」前,先来看如下的例题:

P2871 [USACO07DEC] Charm Bracelet S - 洛谷

有n个物品和一个容量为m的背包,每个物品有重量 w和价值 v两种属性,要求选若干物品放入背包使背包中物品的总价值最大且背包中物品的总重量不超过背包的容量。

在上述例题中,由于每个物体只有两种可能的状态(取与不取),对应二进制中的0和1,这类问题便被称为「0-1 背包问题」。

dp状态f(i,j)为在只能放前i个物品的情况下,容量为j的背包所能达到的最大总价值。

考虑转移。假设当前已经处理好了前i-1个武平的所有状态,那么对于第i个物品:

  • 当其不放入背包时,背包的剩余容量不变,背包中物品的总价值也不变,故这种情况的最大价值为f(i-1,j);
  • 当其放入背包时,背包的剩余容量会减小v,背包中物品的总价值会增大w,故这种情况的最大价值为f(i-1,j-w)+v。

由此可以得出状态转移方程:fi,j=max⁡(fi−1,j,fi−1,j−wi+vi)f_{i,j}=\max(f_{i-1,j},f_{i-1,j-w_i}+v_i)fi,j=max(fi1,j,fi1,jwi+vi)

但是,这里如果直接采用二维数组对状态进行记录,会出现MLE。可以考虑改用滚动数组的形式来优化。

由于对f(i)有影响的只有f(i-1),可以去掉第一维,直接用f(i)来表示处理到当前物品时背包容量为i的最大价值,得出以下方程:fj=max⁡(fj,fj−wi+vi)f_j=\max\left(f_j,f_{j-w_i}+v_i\right)fj=max(fj,fjwi+vi)

大部分背包问题的转移方程都是在此基础上推导出来的。

核心代码就这个状态转移

for(int j=m;j>0;j--){if(w<=j)dp[j]=max(dp[j],dp[j-w]+d);
}

相关练习

P1048 [NOIP 2005 普及组] 采药 - 洛谷

P1049 [NOIP 2001 普及组] 装箱问题 - 洛谷

P1060 [NOIP 2006 普及组] 开心的金明 - 洛谷

278. 数字组合 - AcWing题库

P1164 小A点菜 - 洛谷


完全背包

完全背包模型与0-1背包类似,与0-1背包的区别仅在于一个物品可以选取无限次,而非仅能选取一次。

我们可以借鉴0-1背包的思路,进行状态定义:设f()i,j为只能选前i个物品时,容量为j的背包可以达到的最大价值。

虽然定义与 0-1 背包类似,但是其状态转移方程与0-1背包并不相同。

可以考虑一个朴素的做法:对于第i件物品,枚举其选了多少个来转移。这样做的时间复杂度是O(n3)的。

状态转移方程如下:

fi,j=max⁡k=0+∞(fi−1,j−k×wi+vi×k)f_{i,j}=\max_{k=0}^{+\infty}(f_{i-1,j-k\times w_i}+v_i\times k)fi,j=maxk=0+(fi1,jk×wi+vi×k)

做一个简单的优化。可以发现,对于f(i,j),只要通过f(i,j-w)转移就可以了。因此状态转移方程为:

fi,j=max⁡(fi−1,j,fi,j−wi+vi)f_{i,j}=\max(f_{i-1,j},f_{i,j-w_i}+v_i)fi,j=max(fi1,j,fi,jwi+vi)

与 0-1 背包相同,我们可以将第一维去掉来优化空间复杂度

fj=max⁡(fj,fj−wi+vi)f_{j}=\max(f_j,f_{j-w_i}+v_i)fj=max(fj,fjwi+vi)

完全背包在写的时候根据题目目的的不同还要讨论两层for循环的前后顺序

  • 如果求组合数 就是外层for循环遍历物品,内层for遍历背包
  • 如果求排列数 就是外层for遍历背包,内层for循环遍历物品

完全背包和 0-1 背包的区别就在于对时间大小枚举的顺序不同,0-1背包在内层循环时是倒序的,因为每个物品只能选一次,倒序选择保证了第i个物品只会被放入背包一次;倒序循环满足了0-1背包问题中物品唯一的要求;而完全背包没有数量的限制所以可以正序枚举;


检查数组是否存在有效划分(前缀划分DP)

2369. 检查数组是否存在有效划分 - 力扣(LeetCode)

为了下面一道题能理解完全背包的划分思想,我已先看了这一道题;仔细观察发现居然是麻将!!

我们要在手牌中判断能否凑成对子,刻子和顺子;但是不能排序,所以必须要是子区间才可以;

一开始想的是用前两天学的滑动窗口,但是无法实现,就看题目的提示开始dp;直接套用分析的模板开始分析!

DP五部曲:

  1. 状态定义:

    dp[i] 表示考虑数组的前 i 个元素(即 nums[0]nums[i-1])是否能被有效划分(true/false)。

  2. 状态转移:

    通过三种子数组划分方式进行状态转移:

    • 情况1:最后2个元素相等时,从 dp[i-2] 转移
      dp[i] |= (nums[i-1] == nums[i-2]) && dp[i-2]

    • 情况2:最后3个元素相等时,从 dp[i-3] 转移
      dp[i] |= (i>=3 && nums[i-1]==nums[i-2] && nums[i-2]==nums[i-3]) && dp[i-3]

    • 情况3:最后3个元素连续递增(差值1)时,从 dp[i-3] 转移
      dp[i] |= (i>=3 && nums[i-1]==nums[i-2]+1 && nums[i-2]==nums[i-3]+1) && dp[i-3]

    状态转移方程
    dp[i] = (最后2个相等且dp[i-2]) || (最后3个相等且dp[i-3]) || (最后3个连续递增且dp[i-3])

  3. 初始化(边界值):

    • dp[0] = true; // 空数组视为可划分
    • dp[1] = false; // 单个元素不可划分
  4. 遍历顺序:

    • 正序遍历索引 i2n(含端点)
    • dp[i] 依赖 dp[i-2]dp[i-3],需优先计算小索引
  5. 返回形式:返回 dp[n],表示整个数组能否被有效划分

分析完后,整体代码就呼之欲出

class Solution {
public:bool validPartition(vector<int>& a) {if(a.size()<2)return 0;vector<bool> dp(a.size()+1,0);dp[0]=1;for(int i=2;i<=a.size();i++){bool f1=0,f2=0,f3=0;if(a[i-1]==a[i-2]){f1=dp[i-2];}if(i>2&&a[i-1]==a[i-2]&&a[i-1]==a[i-3]){f2=dp[i-3];}if(i>2&&a[i-1]==a[i-2]+1&&a[i-1]==a[i-3]+2){f3=dp[i-3];}dp[i]=f1||f2||f3;}return dp[a.size()];}
};

从这题开始,下面的才是正宗的完全背包

单词拆分(求排列数)

139. 单词拆分 - 力扣(LeetCode)

铺垫那么久,终于开始引入正题;题目要求用给定的字符串数组中的元素拼出s,不限数量;不要求全部用上,但是拼的时候不能有重叠部分;我们需要判断能否拼出来;里面单词就是物品,字符串s就是背包,单词能否组成字符串s,就是问物品能不能把背包装满。也就是我们所学的完全背包;

DP五部曲:

  1. 状态定义:

    dp[i] : 字符串长度为i的话,dp[i]为true,表示可以拆分为一个或多个在字典中出现的单词。

  2. 状态转移:确定递推公式

    • 如果确定dp[j] 是true,且 [j, i] 这个区间的子串出现在字典里,那么dp[i]一定是true(j < i )。

    状态转移方程

    if ([j, i] 这个区间的子串出现在字典里 && dp[j]是true) dp[i] = true

  3. 初始化(边界值):

    从递推公式中可以看出,dp[i] 的状态依靠 dp[j]是否为true,那么dp[0]就是递推的根基,dp[0]一定为true,否则递推下去后面都都是false了。

  4. 遍历顺序:

    题目中说是拆分为一个或多个在字典中出现的单词,所以这是完全背包。还要讨论两层for循环的前后顺序。这道题需要刚好拼满,不能重叠不能遗漏;所以是求排列数,外层for遍历背包,内层for循环遍历物品。(其实就是为了方便截取字符串去比较)

  5. 返回形式: dp[s.size()]就是最终结果。

class Solution {
public:bool wordBreak(string s, vector<string>& a) {vector<bool> dp(s.size()+1,0);dp[0]=1;for(int i=1;i<=s.size();i++){bool ff=0;for(int j=0;j<a.size();j++){if(a[j].size()<=i){string t=s.substr(i-a[j].size(),a[j].size());if(t==a[j])ff|=dp[i-t.size()];}}dp[i]=ff;}return dp[s.size()];}
};

疯狂的采药(求组合数)

P1616 疯狂的采药 - 洛谷

采草药,每种药没有限制,尽可能总价值越多越好;在这道题目中容易被草药的价值所迷惑;仔细分析总时间是背包,采摘草药所需的时间是物品,我们需要在时间里面分配采摘的情况使得草要的总价值最大;所以与上一题不同,这题需要物品在外,背包在内(求组合数)

DP五部曲:最大化价值

  1. 状态定义:

    dp[j] 表示在时间限制 j 内能获得的最大价值:

    • j 的取值范围:0 到 t(总可用时间)

    • 目标:dp[t] 即时间限制 t 下的最大价值

  2. 状态转移:

    采用完全背包策略(每种药材可采无限次):dp[j] = max(dp[j], dp[j - 采药时间] + 药材价值)

    转移说明:

    • 不采当前药材:保留原值 dp[j]
    • 采当前药材:从 j - 采药时间 转移,加上新价值
    • 转移方程
      dp[j] = max(dp[j], dp[j - a[i].fi] + a[i].se)
  3. 初始化:

    dp[0] = 0; // 0 时间内无法采药,价值为 0
    dp[j] = 0; // 所有初始值设为 0(通过全局数组初始化实现)

  4. 遍历顺序:

    外层循环:遍历药材 i 从 1 到 m(物品维度)

    内层循环:顺序枚举时间ja[i].fit(容积维度)

    从低到高正序遍历(完全背包特性)起点为当前药材所需时间 a[i].fi

  5. 返回形式:输出 dp[t] 即时间上限 t 时的最大价值

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
const int N=1e7+7;
int dp[N];
pii a[N];
void slove(){int t,m;cin>>t>>m;for(int i=1;i<=m;i++)cin>>a[i].fi>>a[i].se;for(int i=1;i<=m;i++){for(int j=a[i].fi;j<=t;j++)dp[j]=max(dp[j],dp[j-a[i].fi]+a[i].se);}cout<<dp[t];
} 
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}

自然数拆分

279. 自然数拆分 - AcWing题库

趁热打铁,再来练一道;一样的思路一样的套路;这题的两种循环效果是一样的,不同顺序视为相同(属于相同的划分方案)

DP五部曲:整数划分问题(方案数统计)

  1. 状态定义

    dp[j] 表示整数 j 可以被划分的方案数(模 2147483648),包括:

    • 使用1到j的整数进行划分
    • 允许重复使用相同的数
  2. 状态转移:

    转移方程dp[j] = (dp[j] + dp[j-i]) % M

    • 含义:对于每个整数i,当前整数j的划分方案数等于:

      不使用 i 的方案数(dp[j])加上使用至少一个 i 的方案数(dp[j-i]

  3. 初始化(边界值)

    dp[0] = 1; // 空划分:用0个数表示0的方案数为1
    dp[i] = 0; // 其他值初始化为0(通过vector初始化)

  4. 遍历顺序

    • 外层循环:枚举使用的数 i 从 1 到 4003(覆盖可能的n值)
    • 内层循环:枚举整数 ji 到 4003(从小到大遍历)
    • 顺序要求:正序枚举背包容量(完全背包模型)
  5. 返回形式

    • 输入目标整数 n

    • 返回(dp[n] - 1) mod M其中:(减1表示排除只包含自身的情况)

      dp[n] 包含划分成单个整数的方案

    • dp[n] 为0时,输出 2147483647(即 M-1

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
const int N=4004;
const int M=2147483648;
int dp[N];
void slove(){int n;cin>>n;dp[0]=1;for(int i=1;i<=n;i++){for(int j=i;j<=n;j++){dp[j]=(dp[j]+dp[j-i])%M;}}int an=dp[n];if(an==0)an=M-1;elsean--;cout<<an<<endl;
} 
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}

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

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

相关文章

Linux驱动06 --- UDP

目录 一、UDP 1.1 介绍 1.2 UDP 的通信方式 1.3 单播 发送函数 接收函数 1.4 广播 1.5 组播/多播 一、UDP 1.1 介绍 传输层的另外一个协议 面向无连接&#xff0c;不稳定&#xff0c;速度快&#xff0c;可以一对多 UDP&#xff08;User Datagram Protocol&…

AJAX 投票:技术解析与应用场景

AJAX 投票:技术解析与应用场景 引言 随着互联网技术的不断发展,Web应用的用户体验越来越受到重视。AJAX(Asynchronous JavaScript and XML)作为一种重要的技术,在实现异步数据交互方面发挥着关键作用。本文将深入探讨AJAX投票系统的技术原理、应用场景以及优化策略。 A…

【字节跳动】数据挖掘面试题0017:推荐算法:双塔模型,怎么把内容精准地推送给用户

文章大纲 双塔模型:推荐算法中的“高效匹配引擎一、双塔模型的核心思想:“分而治之” 的匹配逻辑二、双塔模型的结构:从特征输入到相似度输出1. 输入层:特征的 “原材料处理”2. 塔网络层:用户与物品的“个性化编码”3. 交互层:向量相似度的“偏好打分”三、双塔模型的优…

7月14日日记

数学类今天考完最后一科英语放假回家了。有点羡慕他们。今天英语成绩出来了&#xff0c;我是89分&#xff0c;一开始有点失望&#xff0c;感觉没有上90&#xff0c;这是一个很好的冲击4.0 的机会。但是后来一想好像也没什么可惜的&#xff0c;这个分数还是很高的。舍友小林是90…

js的局部变量和全局变量

全局变量常常定义在函数外&#xff0c;具有全局定义域&#xff0c;在整个js代码的任何地方都可以使用&#xff0c;这个就叫全局变量局部变量定义在函数内部&#xff0c;只在当前函数的定义域可以被使用&#xff0c;而且不同的函数可以定义相同的局部变量&#xff0c;他们之间相…

C++ 多态详解:从概念到实现原理----《Hello C++ Wrold!》(14)--(C/C++)

文章目录前言多态的概念多态的定义和实现虚函数虚函数的重写(覆盖)多态的构成条件override 和 final&#xff08;C11提出&#xff09;finaloverride重载、覆盖(重写)、隐藏(重定义)的对比抽象类接口继承和实现继承多态的原理虚函数表(也叫做虚表)引申:虚表的打印多态的原理静态…

Node.js + Express的数据库AB View切换方案设计

方案总览数据导入过程&#xff1a; - 根据控制表判断当前活跃组&#xff08;假设当前活跃的是a&#xff0c;那么接下来要导入到b&#xff09;。 - 清空非活跃表&#xff08;即b表&#xff09;的数据&#xff0c;然后将新数据导入到b表。 - 切换控制表&#xff0c;将活…

C++_编程提升_temaplate模板_案例

类模板案例案例描述: 实现一个通用的数组类&#xff0c;要求如下&#xff1a;可以对内置数据类型以及自定义数据类型的数据进行存储将数组中的数据存储到堆区构造函数中可以传入数组的容量提供对应的拷贝构造函数以及operator防止浅拷贝问题提供尾插法和尾删法对数组中的数据进…

Win11系统安装Anaconda环境极简教程

Win11系统安装Anaconda环境极简教程 &#x1f4e5; 第一步&#xff1a;下载 Anaconda 安装包 打开浏览器&#xff0c;访问 Anaconda 官网&#xff0c;选择View All Installers 选择所需版本&#xff08;此文以2024.02-1为例&#xff09;&#xff0c;点击进行下载&#xff08;…

Datawhale AI夏令营-基于带货视频评论的用户洞察挑战赛

一.赛事目标基于星火大模型Spark 4.0 Ultra&#xff0c;对视频和评论的数据进行商品识别&#xff0c;情感分析&#xff0c;归类分析&#xff0c;最终为带货效果进行评价。并通过优化模型来提高评价准确度二.赛事环境1.基础平台&#xff1a;星火大模型Spark 4.0 Ultra2.赛事数据…

如何基于FFMPEG 实现视频推拉流

文章目录 前言环境准备为什么选择 FFmpeg什么是nginx 1.7.11.3 GryphonNginx的conf配置启动nginx推流命令接收视频流Untiy播放视频流最后前言 我们经常会有在电脑上实现推拉流的需求,Unity 和Unreal 都提供了基于WebRTC 的视频流方案,效果还不错,但是当我们需要推拉整个电脑…

飞算JavaAI:从情绪价值到代码革命,智能合并项目与定制化开发新范式

目录一、飞算 JavaAI 是什么&#xff1f;二、飞算JavaAI&#xff1a;安装登录2.1 IDEA插件市场安装&#xff08;推荐&#xff09;2.2 离线安装包三、飞算JavaAI核心功能&#xff1a;一键生成完整工程代码功能背景3.1 理解需求3.2 设计接口3.3 表结构自动设计3.4 处理逻辑&#…

Python 基础语法与数据类型(十一) - 类 (class) 与对象 (实例)

文章目录1. 什么是类 (Class)&#xff1f;1.1 定义一个类2. 什么是对象 (Object) 或实例 (Instance)&#xff1f;2.1 创建对象&#xff08;实例化&#xff09;3. 访问属性和调用方法4. 类属性 vs 实例属性5. self 的重要性总结练习题练习题答案前几篇文章我们学习了变量、数据类…

精准数据检索+数据飞轮自驱优化,彩讯AI知识库助力企业知识赋能和效率创新

近两年&#xff0c;人工智能技术的精细化发展&#xff0c;让知识库概念重新成为“热门词汇”&#xff0c;腾讯ima等智能工作台产品为个人用户打造专属知识库&#xff0c;而面向B端市场&#xff0c;企业AI知识库也逐步成为企业集中存储与管理核心文档、数据、经验和流程的知识中…

打破空间边界!Nas-Cab用模块化设计重构个人存储逻辑

文章目录前言1. Windows安装Nas-Cab2. 本地局域网连接Nas-Cab3. 安装Cpolar内网穿透4. 固定Nas-Cab 公网地址"数据管理不该受制于硬件形态或地理边界。这个开源方案证明&#xff1a;当功能模块化且可扩展时&#xff0c;私有云可以像水一样渗透进所有设备——现在就去Git仓…

Sigma-Aldrich细胞培养基础知识:细胞培养的安全注意事项

细胞培养实验室风险评估风险评估的主要目的是防止人员受伤&#xff0c;保护财产&#xff0c;并避免对个人和环境的伤害。在许多国家&#xff0c;法律要求进行风险评估。例如&#xff0c;英国的《英国职业健康与安全法案&#xff08;1974年&#xff09;》就是一个例子。欧洲共同…

Imx6ull用网线与电脑连接

理解工作方式没有路由器时&#xff0c;可以使用&#xff0c;只要保持虚拟机的两个网卡一个与电脑在同一网,一个与板子在同一网段(保持通信)就可以从虚拟机往板子下载第一步&#xff1a;查看电脑连接的网络这一步是在找到主机ip地址这两步在其他同类教程里一样的第二步:设置以太…

力扣454.四数相加Ⅱ

给你四个整数数组 nums1、nums2、nums3 和 nums4 &#xff0c;数组长度都是 n &#xff0c;请你计算有多少个元组 (i, j, k, l) 能满足&#xff1a;0 < i, j, k, l < nnums1[i] nums2[j] nums3[k] nums4[l] 0示例 1&#xff1a;输入&#xff1a;nums1 [1,2], nums2 …

Joplin:一款免费开源、功能强大且注重隐私的笔记软件

Joplin 是一款免费开源、功能强大且注重隐私的笔记和待办事项应用程序&#xff0c;它的设计目标是成为 Evernote 等流行笔记应用的强大替代品&#xff0c;尤其适合重视数据所有权和隐私的用户。 功能特性 Joplin 的核心定位与优势如下&#xff1a; 完全开源&#xff1a;代码公…

渗透前四天总结

目录 一.DNS DNS 基本概述 DNS解析过程 二.HTTPS TLS握手过程 RSA加密 对称加密&#xff1a; 非对称加密&#xff1a; RSA加密过程 三.使用xdebug调试php 四.信息收集 一.DNS DNS 基本概述 DNS&#xff1a;域名系统(DomainNameSystem)因特网的一项核心服务&#xf…