算法:计数类dp

文章目录

      • 一、举个栗子
        • 例子1:爬楼梯问题
        • 例子2:不同路径
        • 例子3:计数子序列
      • 二、基本思路
      • 三、典型例题
        • 一、ACWing:900. 整数划分
          • 1、解法一
            • 1.1、状态转移方程
            • 1.2、参考代码 O(n³) 超时
          • 2、解法二:类似完全背包问题
            • 1.1、状态转移方程

  计数类动态规划(Counting DP)是一种用来解决计数问题的动态规划技术,它通常用于求解在给定条件下满足某种性质的组合或序列的总数。 计数类DP问题的特点是要求计算所有可能情况的数量,而不是求最值或是否存在这样的情况。 当然我们在使用计数类dp的时候,没必要去和线性dp区分开来,会用就行。
  我们先举个栗子,来说明计数类dp是个啥,然后再来说明思路。

一、举个栗子

例子1:爬楼梯问题

  假设你在爬楼梯,每次你可以爬1个或2个台阶。给定楼梯的总台阶数n,你有多少种不同的方法可以爬到楼顶?

分析:让我们用dp[i]表示到达第i阶楼梯的方法数量。如果我们考虑最后一步,我们可以从第i-1阶跨一步到达第i阶,或者从第i-2阶跨两步到达第i阶。因此,到达第i阶的方法数是到达第i-1阶和第i-2阶方法数的和。

状态转移方程dp[i] = dp[i-1] + dp[i-2]

初始化dp[1] = 1, dp[2] = 2


例子2:不同路径

  一个机器人位于一个m x n网格的左上角,机器人每次只能向下或向右移动一步。机器人试图达到网格的右下角。问总共有多少条不同的路径?

分析:定义dp[i][j]为到达网格中(i, j)位置的路径数量。要到达(i, j),机器人只能从(i-1, j)向下走,或从(i, j-1)向右走,所以dp[i][j]是这两个来源的路径数之和。

状态转移方程 dp[i][j] = dp[i-1][j] + dp[i][j-1]

初始化:网格的最上方dp[0][j] = 1和最左方dp[i][0] = 1,因为只有一种方式到达。


例子3:计数子序列

  给定一个字符串,计算不同的子序列个数。子序列是从给定序列中删除一些字符(也可以不删除)后形成的新序列。

分析:这个问题稍微复杂一点。我们可以使用dp[i]表示考虑到字符串的第i个字符时的不同子序列个数。对于每个新字符,它可以选择加入之前的子序列中,或者不加入。

状态转移:如果当前字符之前没出现过,dp[i] = 2 * dp[i-1]。如果当前字符之前出现过,需要减去上一次该字符出现时的子序列个数,以避免重复计数。

二、基本思路

  计数类dp的基本思路不好说,因为本质上就是要找到一个状态以及状态转移,使得满足要求计算所有可能情况的数量 并且 避免重复计算。 这也是最难得一步。

计数类DP的基本思路涉及以下几个步骤:

  1. 定义状态:根据问题的特性,合理定义状态,表示达到当前状态的可能情况数量。状态通常涉及考虑的元素个数、已选择元素的性质(如总和、最大值等)以及其他约束条件。

  2. 确定状态转移方程:根据问题的性质,确定从已知状态到未知状态的转移方式。状态转移方程描述了在当前决策下,如何通过之前的状态来计算当前状态的值。

  3. 初始化:正确初始化DP数组,特别是基础情况(通常是递归的边界条件),这些基础情况直接决定了递推的起点。

  4. 计算顺序:根据状态之间的依赖关系确定计算顺序,确保在计算某个状态之前,它依赖的状态已经被计算。

  5. 结果汇总:根据问题要求,从DP数组中提取最终结果。有时可能需要遍历DP数组的某些部分来汇总最终的计数结果。

三、典型例题

一、ACWing:900. 整数划分

900. 整数划分
在这里插入图片描述
  这类问题的难点再于找到状态和状态转移方程,以及看出来它是一个动态规划题。

1、解法一

用题目本身最接的方式去思考:
  对于数n和 数 n+1的划分方法有没有关系?我们定义dp[i]表示数字i的不同划分方法,那么dp[n+1]dp[n]的关系是什么?在直观上考虑n+1n在大小上相差1,因此n+1n的划分关系是:

  • n+1n多了一种n+1本身
  • n+1将1拿出来,剩下的等同于n的划分
  • 拿出来的1,可以与n的某些划分的中的数合并,可以得到新的划分。我们来考察n+1的划分
    • n+1(n,1) , (n-1,2) , (n-2,3) , (n-3,4) , (n-4,5) ···((n+1)/2,(n+1)/2) ,其中()表示一种划分,括号中的前一个数是怎么划分的不管,后一个数就是一个确切的数,并且满足,前一个数的划分的最后一个数≥后一个数。对于n而言,很显然这些划分对于n中都是没有的。因为我们单独枚举了n+1的最后一个数,那么留给前面的数尽管可能在n中存在这样的序列,但是加上了n+1枚举的最后一个数 就不可能存在这样的序列了。这样问题就出现了,我们怎么才可能找到到底有多少种?
      因此既然有问题,我们不妨将关系定义为二维:dp[i][j]表示 数字i的划分中 以 j结尾的划分数量,那么可以定义数字i的划分数量dp[i][0]等于dp[i][1]加到dp[i][i]。那么我们就可以解决了~

  实际上当我们定义为二维的时候,我们就可以发现,我们实际上是枚举划分的最后一个位置来分类讨论求解,在分类讨论时,我们将当时的状态数存下来以便于后续转移!

1.1、状态转移方程
  • 初始化dp[i][i]=dp[i][0]=1,表示i本身这种划分。
  • dp[i][j]+=dp[i-j][m]:其中j>1&&j<=i-j并且m>=j,并且m<=i-j (i-j , j)
  • dp[i][1]=dp[i-1][0](减少计算)
1.2、参考代码 O(n³) 超时
#include<bits/stdc++.h>
using namespace  std;
int dp[1001][1001];//不存在的情况划分数为 0
int main(void){ios_base::sync_with_stdio(false);cin.tie(0);int N;cin>>N;dp[1][0]=dp[1][1]=1;int p=1e9+7;for(int i=2;i<=N;++i){dp[i][i]=1;//i的情况dp[i][1]=dp[i-1][0];dp[i][0]=(dp[i][i]+dp[i][1])%p;//全1的情况for(int j=2;j<=i/2;++j){//i的划分中 以j为尾数for(int m=j;m<=i-j;++m){//遍历i-j中满足条件的所有可能情况,至少要以j结尾dp[i][j]=(dp[i][j]+dp[i-j][m])%p;//只需要i-j的划分以m>=j结尾就行,当然m>i-j就无意义了。}dp[i][0]=(dp[i][0]+dp[i][j])%p;//累计数量}}cout<<dp[N][0];return 0;
}
2、解法二:类似完全背包问题

转化问题去思考:
  我们很容易想到的一个点是,实际上我们在划分时,只需要划分的数字集合不一样就行了,也就是数字集合的总和能达到n就行了,而不需要满足所谓的n1>=n2>=n3···这种形式上的顺序要求。那么这个问题就转化成了一个类似完全背包的问题了:

  • 对于1~n,n个数字想象成n个物品,体积大小就是数字大小,物品可以无限次被装
  • 一个背包只能装满总体积为n的物品

这个问题就完全被转化成为一个完全背包问题,求体积为n的背包有多少种不同的装物品的情况。

在这里插入图片描述
而这里转化为求:目标是达到背包最大承重的情况下,选取物品可以无限次,选取物品的不同种类数。

1.1、状态转移方程

定义dp[i][j]表示考虑前i个物品,物品不限量装满体积为j的背包时,可以装物品的不同情况数。
一般的:

for(int i=1;i<=n;++i)//遍历物品
for(int j=0;j<=n;++j)//遍历体积
for(int k=0;k*i<=j;++k)//遍历物品承装次数dp[i][j]+=dp[i-1][j-k*i];//物品i可以考虑 k 次

但实际上,根据以上推导可以发现,当考虑前i个物品时,体积为j-i的背包,再加一个物品i,就能够求出体积为j时,物品i考虑的所有次数了。因为dp[i][j-i]已经考虑了所有可能的种类数情况,相当于合并。

  • 不加入物品i时种数:dp[i-1][j]

  • 加入物品i但不限次时种数:dp[i][j-i]

  • dp[i][j]=dp[i-1][j]+dp[i][j-i]

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

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

相关文章

博客部署002-centos安装nginx

1、centos 如何安装nginx? 在CentOS系统上安装Nginx的过程相对直接&#xff0c;通常可以通过系统自带的Yum包管理器来安装。以下是安装Nginx的最新稳定版的步骤&#xff1a; 1.1 更新系统软件包 在安装Nginx之前&#xff0c;首先确保系统软件包是最新的&#xff0c;运行…

如何使用GraphQL和Apollo构建一个宝可梦应用

宝可梦是一个由视频游戏、动画系列与电影、交换卡牌游戏以及其他相关媒体组成的日本媒体特许经营权。 在本文中&#xff0c;我们将使用一个宝可梦GraphQL API&#xff0c;该API提供有关不同宝可梦的数据。 我们将使用Apollo和GraphQL来处理数据获取&#xff0c;以及React来构…

谨慎使用通过光纤传输的HDMI光纤线,存严重缺陷

严重缺陷&#xff1a; 1.只能单向传输 只能单向传输&#xff0c;从一端到另一端&#xff0c;和二极管一样&#xff0c;只能单向传输信号。某些情况你需要变更传输方向时&#xff0c;你将欲哭无泪.传统的HDMI线&#xff0c;不带放大器的&#xff0c;都可以双向传输.网上搜索布…

Vue3【进阶】

简介 https://cn.vuejs.org/guide/introduction.html 创建vue3工程 【基于 vue-cli创建】 基本和vue-cli的过程类似&#xff0c;只是选择的时候用vue3创建 【基于vite创建】【推荐】 【官网】https://vitejs.cn/ 【可以先去学一下webpack】 步骤 【https://cn.vitejs.…

#QT项目实战(天气预报)

1.IDE&#xff1a;QTCreator 2.实验&#xff1a; 3.记录&#xff1a; &#xff08;1&#xff09;调用API的Url a.调用API获取IP whois.pconline.com.cn/ipJson.jsp?iphttp://whois.pconline.com.cn/ipJson.jsp?ip if(window.IPCallBack) {IPCallBack({"ip":&quo…

华为海思2024春招数字芯片岗机试题(共9套)

huawei海思2024春招数字芯片岗机试题(共9套&#xff09;&#xff08;WX:didadidadidida313&#xff0c;加我备注&#xff1a;CSDN huawei数字题目&#xff0c;谢绝白嫖哈&#xff09; 题目包含数字集成电路、System Verilog、Verilog2001、半导体制造技术、高级ASIC芯片综合、…

Java实现二叉树(上)

1.树型结构 1.1树型结构的概念 树是一种 非线性 的数据结构&#xff0c;它是由 n &#xff08; n>0 &#xff09;个有限结点组成一个具有层次关系的集合。 把它叫做树是因为它看 起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的 1.2树型结构的特点…

二 maven构建项目

一 Maven的GAVP Maven工程相对之前的工程&#xff0c;多出一组gavp属性&#xff0c;gav需要我们在创建项目的时指定&#xff0c;p有默认值&#xff0c;后期通过配置文件修改。 GAVP是指 GroupId、ArtifactId、Version、Packaging 等四个属性的缩写&#xff0c;其中前三个是必…

c++的学习之路:14、list(1)

本章讲一下如何使用list&#xff0c;代码在文章末 目录 一、list介绍 二、增 三、删 四、查和改 五、交换 六、代码 一、list介绍 首先还是看一看官方文档的介绍如下图&#xff0c;如下方五点&#xff1a; 1. list是可以在常数范围内在任意位置进行插入和删除的序列式…

element-ui result 组件源码分享

今日简单分享 result 组件的源码实现&#xff0c;主要从以下三个方面&#xff1a; 1、result 组件页面结构 2、result 组件属性 3、result 组件 slot 一、result 组件页面结构 二、result 组件属性 2.1 title 属性&#xff0c;标题&#xff0c;类型 string&#xff0c;无默…

网络基础三——初识IP协议

网络基础三 ​ 数据通过应用层、传输层将数据传输到了网络层&#xff1b; ​ 传输层协议&#xff0c;如&#xff1a;TCP协议提供可靠性策略或者高效性策略&#xff0c;UDP提供实时性策略&#xff0c;保证向下层交付的数据是符合要求的的&#xff1b;而网络层&#xff0c;如&a…

【动态规划-状态压缩dp】【蓝桥杯备考训练】:毕业旅行问题、蒙德里安的梦想、最短Hamilton路径、国际象棋、小国王【已更新完成】

目录 1、毕业旅行问题&#xff08;今日头条2019笔试题&#xff09; 2、蒙德里安的梦想&#xff08;算法竞赛进阶指南&#xff09; 3、最短Hamilton路径&#xff08;《算法竞赛进阶指南》&模板&#xff09; 4、国际象棋&#xff08;第十二届蓝桥杯省赛第二场C A组/B组&#…

Java初始——IDEA-web的启动

Tomcat 文件夹作用 bin 启动 关闭的脚本文件 conf 配置 lib 依赖的jar包 logs 日志 temp 临时文件 webapps 存放的网站 Maven 1.在javaweb中&#xff0c;需要使用大量的jar包&#xff0c;手动导入 2.Maven 架构管理工具 核心&#xff1a;约定大于配置 必须按照规则 web idea-we…

2024.4.7

1. 2列火车 #include<myhead.h>pthread_mutex_t m1; pthread_mutex_t m2;void* run(void* arg) {while(1){pthread_mutex_lock(&m1);printf("火车B进入\n");printf("A请等待\n");pthread_mutex_unlock(&m2);sleep(2);} }int main(in…

开发项目接单报价快速计算,报价量化程序

定制化开发&#xff0c;如何计算项目预算&#xff0c;是程序开发者头疼的一个问题。 项目费用谈得过低&#xff0c;就天天加班累死赚不到钱&#xff1b;谈得过高&#xff0c;会导致顾客流失&#xff0c;信誉受损。 项目费用量化可见是多么重要。 下面是一段量化的程序&#…

跟TED演讲学英文:AI isn‘t the problem — it‘s the solution by Andrew Ng

TED英文文稿 文章目录 TED英文文稿AI isnt the problem — its the solutionIntroductionVocabularyTranscriptSummary 2024年4月6日学习吴恩达的这篇演讲 AI isn’t the problem — it’s the solution Link: AI isn’t the problem — it’s the solution Speaker: Andrew …

C语言进阶课程学习记录-第21课 - 宏定义与使用分析

C语言进阶课程学习记录-第21课 - 宏定义与使用分析 宏定义的本质实验-字面量比较宏定义表达式实验-表达式有效性宏的作用域实验-作用域分析内置宏内置宏演示小结 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学…

人工智能 - 服务于谁?

人工智能服务于谁&#xff1f; 人工智能服务于生存&#xff0c;其最终就是服务于战争&#xff08;热战、技术战、经济战&#xff09; 反正就是为了活着而战的决策。 既然人工智能所有结果&#xff0c;来自大数据的分挖掘&#xff08;分析&#xff09;也就是数据的应用&#x…

自动驾驶中的交通标志识别原理及应用

自动驾驶中的交通标志识别原理及应用 附赠自动驾驶学习资料和量产经验&#xff1a;链接 概述 道路交通标志和标线时引导道路使用者有秩序使用道路&#xff0c;以促进道路行车安全&#xff0c;而在驾驶辅助系统中对交通标志的识别则可以不间断的为整车控制提供相应的帮助。比如…

Transformer位置编码详解

在处理自然语言时候&#xff0c;因Transformer是基于注意力机制&#xff0c;不像RNN有词位置顺序信息&#xff0c;故需要加入词的位置信息来显示的表明词的上下文关系。具体是将词经过位置编码(positional encoding)&#xff0c;然后与emb词向量求和&#xff0c;作为编码块(Enc…