当前位置: 首页 > news >正文

DP:数位DP

数位DP的大致思想:枚举每一位能选取的合法值。

1. LC 2376 统计特殊整数

说是DP,但实际上状态转移方程挺难写的,毕竟是枚举+集合论,这里就不贴状态转移方程了。总体的写法其实是搜索+记忆化。之所以称之为DP,是因为:

  1. 对于第i位,
    1. 如果[0,i-1]位全部都选的能选的最大值,那么第i位最多也就只能选到最大值(注意第0位肯定是受限制的)
    2. 否则,第i位能随便选
  2. 如果前面[0,i-1]位都是0(前导零),这一位就可以随便选了

有这两大状态转移的规则,所以还是被称之为DP。细节写在代码里了

import java.util.Arrays;class Solution {char[] s;int[][] memo;public int countSpecialNumbers(int n) {s = Integer.toString(n).toCharArray();// 记搜// memo[i][mask]代表[0,i-1]位选掉了mask中各个索引位置代表的数的情况下后面有多少个特殊整数memo = new int[s.length][1<<10];for (int i = 0; i < memo.length; i++) {Arrays.fill(memo[i],-1); // -1 代表没有计算过}// 一开始第一位当然要受限制,不过也可以是前导零,所以 (true,false)return f(0,0,true,false);}/*** 记搜计算在确定[0,i-1]位后剩下的有种特殊整数的情况* @param i 第i位* @param mask 已经选择了mask集合中的数* @param isLimit 当前位上限是否有限制,有的话是 int(s[i]),没有的话是9* @param isNum 当前位是否跳过,即前导零* @return 确定[0,i-1]位后剩下的有种特殊整数的情况*/private int f(int i,int mask,boolean isLimit,boolean isNum){if(i==s.length){return isNum ?1:0; // 防止从头到尾的前导零,这种情况根本不是个数字,当然不能算}/*这个!isLimit条件非常重要,举个例子,n=420假设现在前两位选了 4,2,那么mask = {4,2},第三位就只能选0但如果前两位选了2,4,mask={4,2},第三位可以随便选(除了2,4)在第一种情况下能选1种,第二种情况下能选8种(10-2),差了7种情况而之后的循环枚举会考虑所有顶着最大上限选的情况,所以如果当前这个数位受到最大上限的限制的话,后面的for循环会统计这个情况的而不是把非受限制的情况的记忆化搜索结果返回,在这个例子里dp[2][{2,4}] = 8 而不是1但isNum不是必须判断的,因为如果前面都受限制,后面一位也一定受限制,可前面都是前导零,不代表后面也得是前导零况且,[0,i-1]位全是前导零的情况,顶多出现一次,后面再怎么递归都不可能有的*/if(!isLimit && memo[i][mask]!=-1){return memo[i][mask];}int res = 0;if(!isNum){// 含前导零,跳过res = f(i+1,mask,false,false);}// 当前位置是否受限制int upper = isLimit?s[i]-'0':9;// 枚举可以填充的数,这里要检查是否之前使用了前导零,使用了的话要去掉(之前if(!isNum)加过了),没有用的话可以从0开始for (int j = isNum?0:1; j <= upper; j++) {// 特殊整数要求各数位都不一样,所以检查mask中之前用过没// mask就是个位图,比如 binary(mask) = 0101010111 从右往左看,用集合论表示就是 {8,6,4,2,1,0} 这些数字已经被用过了if((mask>>j&1)==0){// 用掉j就是把mask的第j位设置为0 mask|(1<<j)// 那么下一个数位是否受限制呢?如果当前数位受限制并且当前数位选取了上限,那么下一位是受限制的,否则不受限制// 例如,n = 123 i=1,假设当前数位不受上限,那么也就是说这个数<=99,下一位当然也不受限制// 但若受限制,则代表前面i=0的时候选择了上限1,i=1选择最多是2,下一位自然受限制,最大是3// 最后,既然这一位枚举了一位有意义的数字,后续的枚举自然也是有意义的, isNum 为 trueres += f(i+1,mask|(1<<j),isLimit && j==upper,true);}}// 记忆化if(!isLimit){memo[i][mask] = res;}return res;}
}

2. LC 233 数字1的个数

这题是我学了板子后做的第一题,真的汗流浃背。想想做做调调,卡了1h才出来。

首先这个跟上面一题的区别是,对于枚举的数字没有限制。不仅没有重复性的限制,而且还没有前导零的限制(前导零的数不会被判无效,因为这道题只看1的数量)

这样就简便了不少。我们只需要看是否被上限限制即可。这个是否受限还是和以前一样,只有前面的都受限,本次才会受限,否则不会。

令f(i,isLimit)表示第[i,n-1]位在Limit的限制下能产生的1的数量。那么本轮的上限可以由isLimit计算得出:

  1. 如果upper<1,也就是upper=0的情况,本轮不可能选1

  2. 如果upper==1,本轮选择1会产生 int(suffix(n,i+1))+1 个1。其中suffix(n,i+1)代表n的[i+1,n-1]位的值,例如 n = 2132 , i=1 那么suffix(2232,2) = 32。

    这是比较显然的,拿上面那个例子来说,如果本轮选择1,后续会有2100到2132这些数的第i=1位是1,所以就是32-00+1=33个

  3. 如果upper>1,本轮选择1会产生 pow(10,n-i-1)个1。例如n=2232,i=1,那么如果本轮选择1,就会有2100到2199的第i=1位是1,也就是pow(4-1-1)=100个

以上考虑的是本轮(第i位)产生的1的数量。后面[i+1,n-1]产生的还没算:

两种情况讨论。上限是upper,说明本轮有upper种选择,其中可能有一种是顶格选的(isLimit情况),有upper种是非顶格选的。依次累加到res即可。这里对于前者,根据当前的isLimit来(如果当前顶格了后面也得顶格,当前不顶格后面也不顶格),根据后者,isLimit = false

最后,记搜的时候要记得排除顶格选的情况。因为这种情况已经被统计过了。

import java.util.Arrays;class Solution {char[] s;int[] memo;public int countDigitOne(int n) {s = Integer.toString(n).toCharArray();memo = new int[s.length];Arrays.fill(memo,-1);return f(0,true);}/*** 记搜计算[0,i-1]位选择完毕后,后面的位置总共能出现多少个1* @param i 第i位* @param isLimit 受到最大上限限制与否* @return [0,i-1]位选择完毕后,后面的位置总共能出现多少个1*/private int f(int i,boolean isLimit){if(i==s.length){return 0;}/*例如:n = 1230 ,现在 i=[0,1,2] = {1,2,3},那么后面一个1都不可能有但如果i=[0,1,2] = {1,2,2},后面是可以有一个1的memo记录的是后者*/if(!isLimit && memo[i]!=-1){return memo[i];}int res = 0;int upper = isLimit?s[i]-'0':9;// 如果这一轮选1if(upper==1){res += suffix(i+1)+1;}else if(upper>1){res += (int) Math.pow(10,s.length-i-1);}// 本来可以选 upper+1个数(这一轮)// 如果之前全部都顶格选了,那么将是upper个可以后续不用顶格选的,和一个必须顶格选的res += upper*f(i+1,false) + f(i+1,isLimit);if(!isLimit){memo[i] = res;}return res;}private int suffix(int start){StringBuilder sb = new StringBuilder();for(int i=start;i<s.length;i++){sb.append(s[i]);}return sb.isEmpty()?0:Integer.parseInt(sb.toString());}
}

3. LC 2719 统计整数数目

这道题我思路有的,但就是有点歪,所以虽然A了但是时间上表现不好

首先我的记搜是包含4个状态的:定义 f (i,isLower,isUpper,acc)表示在[0,i-1]位均已枚举,且数位和为acc,且是(否)受下限与上限的制约的情况下,后续能够产生的符合条件的数。

那么上限和下限分别怎么算?我通过补齐较小的num1的前导零,使其与num2在数位长度上等长。这样下限由num1(补齐前导零后)决定,上限由num2决定。

在深搜时记忆化在不受上下限制约的情况下,在枚举到第i位且已有数位累计和acc的情况下,后续能有多少个符合条件的数。

之后根据是否受上下限制约枚举数位即可。这里注意枚举时可以及时地判断是否已经爆掉数位和上界了,而下界可以留到最终递归基的是否判断。

最后,我现在是觉得,模运算这个东西,有很强的性质(加法乘法的性质都特别强),如果担心答案爆了怎么办,就在能取模的地方全部取模就行。

import java.util.Arrays;class Solution {static long mod = (long)1e9+7;char[] s1;char[] s2;long[][] memo;int min;int max;public int count(String num1, String num2, int min_sum, int max_sum) {min = min_sum;max = max_sum;s1 = supplyLeadingZero(num1,num2).toCharArray();s2 = num2.toCharArray();// memo[i][acc]代表在不受限制的情况下 到了第i位已经有acc的数位和,第[i+1,n-1]位最多能有多少个符合条件的数memo = new long[s2.length][22*9+1];for (int i = 0; i < memo.length; i++) {Arrays.fill(memo[i],-1L);}return (int) (f(0,true,true,0) % mod);}private String supplyLeadingZero(String num1,String num2){StringBuilder num1Builder = new StringBuilder(num1);while(num1Builder.length()<num2.length()){num1Builder.insert(0, "0");}num1 = num1Builder.toString();return num1;}private long f(int i,boolean isLower,boolean isUpper,int acc){if(i==s2.length){return acc>=min?1L:0L;}if(!isLower && !isUpper && memo[i][acc]!=-1){return memo[i][acc];}int lb = isLower?s1[i]-'0':0;int ub = isUpper?s2[i]-'0':9;long res = 0L;for(int j=lb;j<=ub;j++){if(max>=acc+j){res = (res%mod + f(i+1,isLower && j==lb, isUpper && j==ub, acc+j)%mod) % mod;}}if(!isLower && !isUpper){memo[i][acc] = res;}return res;}
}

还有一种更常见的思路是,先统计一遍≤num1的情况,再统计一遍≤num2的情况,然后后者减前者就是(num1,num2]的情况。又因为题目是闭区间,所以单独判一下num1符合条件与否即可。这种思路跑得比我的代码快,这里摘录一份:

class Solution {private static final int MOD = 1_000_000_007;public int count(String num1, String num2, int minSum, int maxSum) {int ans = calc(num2, minSum, maxSum) - calc(num1, minSum, maxSum) + MOD; // 避免负数int sum = 0;for (char c : num1.toCharArray()) {sum += c - '0';}if (minSum <= sum && sum <= maxSum) {ans++; // num1 是合法的,补回来}return ans % MOD;}private int calc(String s, int minSum, int maxSum) {int n = s.length();int[][] memo = new int[n][Math.min(9 * n, maxSum) + 1];for (int[] row : memo) {Arrays.fill(row, -1);}return dfs(0, 0, true, s.toCharArray(), minSum, maxSum, memo);}private int dfs(int i, int sum, boolean isLimit, char[] s, int minSum, int maxSum, int[][] memo) {if (sum > maxSum) { // 非法return 0;}if (i == s.length) {return sum >= minSum ? 1 : 0;}if (!isLimit && memo[i][sum] != -1) {return memo[i][sum];}int up = isLimit ? s[i] - '0' : 9;int res = 0;for (int d = 0; d <= up; d++) { // 枚举当前数位填 dres = (res + dfs(i + 1, sum + d, isLimit && (d == up), s, minSum, maxSum, memo)) % MOD;}if (!isLimit) {memo[i][sum] = res;}return res;}
}

相关文章:

DP:数位DP

数位DP的大致思想&#xff1a;枚举每一位能选取的合法值。 1. LC 2376 统计特殊整数 说是DP&#xff0c;但实际上状态转移方程挺难写的&#xff0c;毕竟是枚举集合论&#xff0c;这里就不贴状态转移方程了。总体的写法其实是搜索记忆化。之所以称之为DP&#xff0c;是因为&am…...

js逆向第21例:猿人学第20题新年挑战

文章目录 一、前言二、定位加密参数1、定位wasm加密2、反编译wasm3、定位sign加密三、代码实现四、参考文献一、前言 新春福利:抓取这5页的数字,计算加和并提交结果 二、定位加密参数 通过get请求地址可以看到需要搞定参数有page、sign、t如下图: 进入堆栈不难发现这样一…...

贪心+蓝桥杯

原题路径 题目思路 : 思路很简单&#xff0c;肯定是贪心做法&#xff0c;要使总代价最小&#xff0c;需用那些出现次数比avg多的数来替换那些没有出现或者是出现次数少于avg的数, 所以我们存当前数每次出现的代价是多少 ,枚举每一个 0 - 9 之间的数 &#xff0c;如果当前数出现…...

第二篇:新建node项目并运行

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 安装 Node.js&#xff1a;首先&#xff0c;确保你的…...

阳光保险选择OceanBase稳定运行超700天

阳光保险集团成立于 2005 年 7 月&#xff0c;旗下拥有财产保险、人寿保险、信用保证保险、资产管理等多家专业子公司&#xff0c;是全球市场化企业中成长最快的集团公司之一&#xff0c;目前位列中国保险行业前八。随着数字化升级趋势的不断加速&#xff0c;很多企业产生将软硬…...

最强大脑闪电心算草稿1

#include<bits/stdc.h> #include<windows.h> using namespace std; int main() {double speed,n,op,sum0;int ans;srand(time(NULL));cout<<"请输入加(1)/减(2)/加减混合(3):";cin>>op;cout<<"请输入题目数量:";cin>>…...

融优学堂-艺术史

导论4 1.【单选题】根据导论的讲解&#xff0c;下列表述正确的是&#xff08;&#xff09;。&#xff08;1&#xff09;艺术品是因人的活动而被创造出来的人工制品。&#xff08;2&#xff09;许多物品被制造出来时&#xff0c;最初的目的是满足某种实用的用途&#xff0c;而不…...

༺༽༾ཊ—设计-七个-07-原则-模式—ཏ༿༼༻

第七原则&#xff1a;迪米特职责 类与类之间的耦合度尽可能低 换言之&#xff0c;我们可以理解成———只与直接朋友说话&#xff0c;不跟陌生人说话 直接朋友&#xff1a; 通过方法传参传进来的朋友&#xff0c; 类自己的字段&#xff0c; 构造函数进来的也是直接朋友&…...

一篇文章带你搞懂---全排序

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C/C》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 全排序&#xff08;Permutation&#xff09;是指将一组元素按照一定的顺序进行排列的过程。在计算机科学中&#xff0c;全排序是一…...

提升问题检索的能力

事实上&#xff0c;在信息极度丰富的时代&#xff0c;信息检索和筛选能力格外重要。一些搜索引擎的出现已极大地方便了我们日常的信息检索&#xff0c;此处需要注意的是我们不能仅仅局限于常见的搜索引擎&#xff0c;也需要关注和积累一些专业平台或是具有集成功能的引擎&#…...

软件测试|SQLAlchemy query() 方法查询数据

简介 上一篇文章我们介绍了SQLAlchemy 的安装和基础使用&#xff0c;本文我们来详细介绍一下如何使用SQLAlchemy的query()方法来高效的查询我们的数据。 创建模型 我们可以先创建一个可供我们查询的模型&#xff0c;也可以复用上一篇文章中我们创建的模型&#xff0c;代码如…...

FlinkCDC的分析和应用代码

前言&#xff1a;原本想讲如何基于Flink实现定制化计算引擎的开发&#xff0c;并以FlinkCDC为例介绍&#xff1b;发现这两个在表达上不知以谁为主&#xff0c;所以先分析FlinkCDC的应用场景和技术实现原理&#xff0c;下一篇再去分析Flink能在哪些方面&#xff0c;做定制化计算…...

序章 搭建环境篇—准备战士的剑和盾

第一步&#xff1a;安装node.js Node.js 内置了npm&#xff0c;只要安装了node.js&#xff0c;就可以直接使用 npm&#xff0c;官网地址&#xff1a; Download | Node.js 在这里不建议安装最新版本的node.js&#xff0c;可以选跟我一样的版本&#xff0c;node版本v16.13.2 链…...

【C++】vector的使用及模拟实现

目录 一、vector的介绍及使用1.1 介绍vector1.2 vector的使用1.2.1 构造1.2.2 遍历访问1.2.3 容量空间1.2.4 增删查改 二、vector的模拟实现2.1 成员变量2.2 迭代器相关函数2.3 构造-析构-赋值重载2.3.1 无参构造2.3.2 有参构造12.3.3 有参构造22.3.4 拷贝构造2.3.5 赋值重载2.…...

【数据库】sql优化有哪些?从query层面和数据库层面分析

目录 归纳sql本身的优化数据库层面的优化 归纳 这类型问题可以称为&#xff1a;Query Optimization&#xff0c;从清华AI4DB的paper list中&#xff0c;该类问题大致可以分为&#xff1a; Query RewriterCardinality EstimationCost EstimationPlan Optimization 从中文的角…...

nginx基本优化

安装nginx隐藏版本号 查看百度web服务器 [rootcjq11 ~]# curl -I http://www.baidu.com 隐藏nginx服务器版本号 [rootcjq11 ~]# cd /usr/local/src/nginx-1.22.0/ [rootcjq11 nginx-1.22.0]# vim src/core/nginx.h第13、14行修改版本号和服务器名称 [rootcjq11 nginx-1.2…...

软件测试|使用selenium处理单选框和多选框

简介 我们在web自动化测试工作中&#xff0c;经常会遇到对单选框&#xff08;Radio Buttons&#xff09;或者多选框&#xff08;Checkboxes&#xff09;进行操作的场景&#xff0c;单选框和多选框主要是用于我们做出选择或提交数据。本文将主要介绍selenium对于单选框和多选框…...

openssl3.2 - EVP_CIPHER_fetch算法名称字符串(参数2)的有效值列表

文章目录 openssl3.2 - EVP_CIPHER_fetch算法名称字符串(参数2)的有效值列表概述如何找到算法名称字符串列表?openssl-3.2.0\providers\implementations\include\prov\names.h备注END openssl3.2 - EVP_CIPHER_fetch算法名称字符串(参数2)的有效值列表 概述 进行加解密时, 先…...

vue3中的hook公共函数封装及运用

vue3 中的 hooks 就是函数的一种写法&#xff0c;就是将文件的一些单独功能的js代码进行抽离出来&#xff0c;放到单独的js文件中&#xff0c;或者说是一些可以复用的公共方法/功能 使用Vue3的组合API封装的可复用的功能函数自定义hook的作用类似于vue2中的mixin技术自定义Hook…...

广州市工信局、天河区商务金融局及广州专精特新促进会走访思迈特

2024年1月11日下午&#xff0c;广州市工信局、天河区商务金融局及广州专精特新促进会相关负责人莅临广州思迈特软件总部调研指导&#xff0c;思迈特软件总裁兼COO姚诗成代表公司热情接待&#xff0c;并陪同调研。 调研组实地参观了思迈特软件&#xff0c;深入了解了思迈特发展历…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局&#xff1a;刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断"&#xff0c;医生需通过显微镜观察组织切片&#xff0c;在细胞迷宫中捕捉癌变信号。某省病理质控报告显示&#xff0c;基层医院误诊率达12%-15%&#xff0c;专家会诊…...

第7篇:中间件全链路监控与 SQL 性能分析实践

7.1 章节导读 在构建数据库中间件的过程中&#xff0c;可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中&#xff0c;必须做到&#xff1a; &#x1f50d; 追踪每一条 SQL 的生命周期&#xff08;从入口到数据库执行&#xff09;&#…...

LOOI机器人的技术实现解析:从手势识别到边缘检测

LOOI机器人作为一款创新的AI硬件产品&#xff0c;通过将智能手机转变为具有情感交互能力的桌面机器人&#xff0c;展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家&#xff0c;我将全面解析LOOI的技术实现架构&#xff0c;特别是其手势识别、物体识别和环境…...

怎么开发一个网络协议模块(C语言框架)之(六) ——通用对象池总结(核心)

+---------------------------+ | operEntryTbl[] | ← 操作对象池 (对象数组) +---------------------------+ | 0 | 1 | 2 | ... | N-1 | +---------------------------+↓ 初始化时全部加入 +------------------------+ +-------------------------+ | …...

【实施指南】Android客户端HTTPS双向认证实施指南

&#x1f510; 一、所需准备材料 证书文件&#xff08;6类核心文件&#xff09; 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...