蓝桥杯每日一题-----数位dp

前言

今天浅谈一下数位dp的板子,我最初接触到数位dp的时候,感觉数位dp老难了,一直不敢写,最近重新看了一些数位dp,发现没有想象中那么难,把板子搞会了,变通也会变的灵活的多!

引入

以一道例题作为数位dp的引入,题目如下,链接
在这里插入图片描述
数据范围为1e9,一般的算法很难把这道题拿下,类似求在一段区间范围内,满足某些条件的数字的个数,并且数据范围很大时就会联想到数位dp算法。

第一个板子

我遇到的数位dp板子有三个,第一个来源于y总的讲解,附上链接和这道题的代码,y总的讲解逻辑清晰,想学习可以直接看视频。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Arrays;
import java.util.Scanner;public class Main {static int[][] f = new int[11][10];static int k;
public static void main(String[] args) throws IOException {StreamTokenizer sc=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));sc.nextToken();//Scanner scanner = new Scanner(System.in);int t = (int)sc.nval;while(t> 0) {t--;sc.nextToken();k = (int)sc.nval;sc.nextToken();int l = (int)sc.nval;sc.nextToken();int r = (int)sc.nval;//int l = scanner.nextInt();//int r = scanner.nextInt();for (int i = 0; i < 11; i++) {Arrays.fill(f[i], 0);}init();System.out.println(dp(r) - dp(l - 1));//dp(r);}return;
}private static int dp(int num) {// TODO Auto-generated method stubif(num == 0) {return 0;}int res = 0;int last = 0;//上一个位数的数字int[] nu = new int[12];int n = 1;while (num > 0 ) {nu[n++] = num%10;num = num / 10;}n--;//System.out.println(n);for (int i = n; i > 0; i--) {//遍历位数int x = nu[i];int jj;if(i == n) {jj = 1;}else {jj = 0;}//System.out.println(x);//System.out.println(last);for (; jj < x; jj++) {//遍历该位数上可以填的数字if(Math.abs(jj - last) <= k || i == n) {//System.out.println("mm" + i);res += f[i][jj];}}if(Math.abs(x-last) <= k || i == n) {last = x;//System.out.println("1111");}else {break;}if(i==1) {res++;}}//加包含前导0的,其实就是加上不是和num同位数的数字,for (int i = 1; i < n; i++) {for (int j = 1; j < 10; j++) {//从1开始res += f[i][j];}}//System.out.println(res);return res;
}private static void init() {// TODO Auto-generated method stubfor (int i = 0; i < 10; i++) {//初始化只有一位数字的时候,一定符合要求f[1][i] = 1;//注意i一定从0开始}for (int i = 2; i < 10; i++) {//初始化其它位数的数字for (int j = 0; j < 10; j++) {//注意,这里可以包含0for (int m = 0; m < 10; m++) {if(Math.abs(m-j) <= k) {f[i][j] += f[i-1][m];}}}}
}
}

第二个板子

  1. 求区间[L,R]内符合条件的数字,转换为求区间[0,L-1]与[0,R]内符合条件的数字个数,答案就是这两个做差。
  2. 在遍历数字的时候,先遍历数字的位数,假设我要求[0,R]内满足条件的数字,R的位数为cnt,在遍历cnt的时候我要注意,不能超过R,如果R是23456,那么第cnt位能够选择的数字范围是[0,2]。如果我第cnt位选择了1,在遍历cnt-1位的时候我就不需要考虑遍历数字是否越界问题,因为cnt位已经决定了后面的位数怎么选都不会比23456大。如果第cnt位选择了2,在遍历cnt-1位时我可以填的范围就变成了[0,3],所以我需要有一个变量记录当前我前面选择的数字是否是贴上界的,令这个变量为limit,如果limit=1,当前位数可以选择的数字上界是a[cnt],否则就是9,其中数组a是把23456拆分的结果,拆分代码如下
int cnt = 0;while(n > 0) {a[++cnt] = (int) (n%10);n/=10;}

根据上述分析,会有如下代码,

int up = limit==1?a[cnt]:9;//求当前位数最大能够填的数字
limit&(i==up?1:0)//下一个limit是否还等于1,i表示当前位数填的数字,如果填了最大的那个数,且当前的limit是1,则填下一位数时能够填的最大数字也是受约束的

up表示当前位数可以填的数字上界。

  1. 接下来处理一下前导零,如果前导零的存在不会影响结果,那么不需要处理,否则就要处理一下前导零。比如求包含2和4的数字个数就不需要处理前导0,因为他不影响结果。如果要求数字各个位数不为0
    假设我要求[0,R]内满足条件的数字,R的位数为cnt,在遍历cnt的时候我要注意,在这个位置填的0就是前导0,如果我在这个位置填了0,再去遍历第cnt-1位数字时,这里填的0还是前导0.如果我第cnt位没有填0,那么我在cnt-1位填的0就不是前导0,他是有效的一维数,就像001和101一样,00里面的0都是前导0,是无效的。而10里的0是十位上的0,是有效的。我们用zeros来表示当前位的0是否是前导0,第cnt位的0肯定是前导0,如果第cnt位填了0,第cnt-1位的0才是前导0,否则就不是。所以有

    zeros&(i==0?1:0)//表示下一位的zeros是否是0,i表示当前位填的数字,如果当前位填了0并且当前位的zeros是1,那么下一位的zeros也是1.

    前导0的使用要比上界limit的使用更灵活一点,他是根据题目的要求来使用的。

  2. 这里主要讲记忆化递归。因为这一个板子用的是dfs,而dfs可能会有超时的问题,所以就有了记忆化递归。记忆化递归要标记好当前的状态,所以用来记忆当前状态的数组也是要根据题目设定。
    当题目中有多个测评样例时,进行记忆化的时候要注意不要记住在特定数下的答案。所以有下面的代码

    if(limit == 0) dp[cnt][last][zeros] = res;

    为什么要在limit=0时才进行记忆化呢?因为limit=1说明当前的数是贴上界的,而这个数是受当前这个样例影响的,比如2345这个数字,在遍历到百位数字3时,我是贴上界的,如果千位数字是2,那么此时十位数字只能选0-4,此时得到的res是从0-4递归得到的。但是如果换一个数字2377,在遍历到百位数字3时,我如果是不贴上界的,可选的数字应该是0-9,如果是贴上界的,可选的数字是0-7,明显此时的状态dp[3][3][0]和数字2345的转移是不一样的。所以我只有不贴上界的时候,说明后面的转移都是0-9时才进行记忆。
    读取记忆的时候也是同样的道理,

    if(limit==0&&dp[cnt][last][zeros]!=-1) { return dp[cnt][last][zeros]; }

    只有此时不贴上界的时候才能读取之前的记忆。
    记忆化数组根据具体的题目来设定,并不是统一的,具有高度灵活性,只要解释起来没问题就可以。像小明数这道题没有记忆limit,有些情况也是可以记忆limit的。

分析题目

针对小明数而言,前导0会影响答案,为什么?题目要求相邻两数之差绝对值不超过k,如果存在前导0并且不加以判断那么会认为前导0也是有效数字,那么0后面只能填0-k,但实际前导0应该是无效数字,前面一个数字是前导0,后面可以填0~9中的任意一个数字。如果没有判断前导零,会导致结果比实际的小。 求某些数字相邻位数上的数字之差的绝对值不超过k,来想一下dfs的时候需要什么,必然要有的是当前的位数cnt和是否贴上界limit,刚刚也分析了需要判断前导零,所以有zeros。遍历到cnt位时,来判断一下当前位可以填啥,我要满足相邻位数上的数字之差的绝对值不超过k,我得知道前一个位数我填的是几,所以就有了last,指示前一个位数上填的数字。然后就没有其它的了,所以 dfs(cnt,last,zeros,limit),接下来就直接放代码了。

import java.util.Arrays;
import java.util.Scanner;
public class Main {static int dp[][][] = new int[20][20][2];//还要记录当前的状态是不是有前导零!!!!!!!static int a[] = new int[20];static int k,ans;static int nums[] = new int[20];
public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int t = scanner.nextInt();while(t-- > 0) {for(int i = 0;i < 20;i++)for(int j = 0;j < 20;j++)Arrays.fill(dp[i][j],-1);k = scanner.nextInt();long l = scanner.nextLong();long r = scanner.nextLong();System.out.println(solve(r)-solve(l-1));private static int solve(long n) {// TODO Auto-generated method stubint cnt = 0;while(n > 0) {a[++cnt] = (int) (n%10);n/=10;}return dfs(cnt,1,-1,1);
}private static int dfs(int cnt, int limit, int last,int zeros) {//前导0对答案有影响!!!!!!// TODO Auto-generated method stubif(cnt==0) {return 1;}if(limit==0&&dp[cnt][last][zeros]!=-1) {return dp[cnt][last][zeros];}int res =0;int up = limit==1?a[cnt]:9;for(int i = 0;i <= up;i++) {if(Math.abs(last-i)<=k||zeros==1) {//3 1 2 0 dp[1][0]nums[cnt] = i;res += dfs(cnt-1, limit&(i==up?1:0), i,zeros&(i==0?1:0));//120}}if(limit == 0) dp[cnt][last][zeros] = res;	     return res;
}
}

如果代码有问题,欢迎在评论区内提出来!第三个板子就不讲啦,其实就是把第二个板子的dfs变成dp,但是个人感觉没有dfs灵活,目前在用第二个板子。

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

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

相关文章

基于Python的深度学习的身份证识别考勤系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

如何使用第三方API采集电商数据呢?

电商商家最常唠叨的就是店铺运营难做。每日多平台店铺数据统计汇总繁琐耗时&#xff0c;人工效率偏低&#xff0c;且工作内容有限。 特别是眼下“618&#xff0c;双十一&#xff0c;双十二&#xff0c;年底大促”将至&#xff0c;如何提高运营的效率和质量、保证产品及服务的良…

深度学习-基础过关

众所周知&#xff0c;机器学习是一门跨学科的学科&#xff0c;主要研究计算机如何通过学习人类的行为和思维模式&#xff0c;以实现某些特定的功能或目标。它涉及到概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科&#xff0c;使用计算机作为工具并致力于真实实时的…

大数据信用报告在线查询平台哪个好?

随着大数据技术在金融风控的运用&#xff0c;大数据信用越来越被人熟知&#xff0c;由于线下没有查询大数据信用的地方&#xff0c;想要查询大数据信用报告只有在线上查询&#xff0c;那大数据信用报告在线查询平台哪个好呢?本文贷你一起去了解市面上比较好的三个平台。 大数据…

[自动驾驶算法][从0开始轨迹预测]:三、常用的轨迹预测数据集--Argoverse v2

文章目录 1. 轨迹数据集总览2. Argoverse v2数据集2.1 传感器布局与坐标系统2.2 轨迹预测数据集1. 数据集的下载和读取2. 场景文件的数据结构&#xff1a;3. 地图文件的数据结构 写在前面&#xff1a; “工欲善其事&#xff0c;必先利其器”&#xff01; 在深度学习中&#xff…

x-shell安装、使用以及配置cuda、cudnn和conda

x-shell安装、使用以及安装最新版本conda x-shell安装远程连接服务器conda安装和环境配置 x-shell安装 x-shell是一款终端模拟软件&#xff0c;用于在Windows界面下远程访问和使用不同系统下的服务器。免费版本下载地址&#xff1a; https://www.xshell.com/zh/free-for-home-…

通过ETLCloud CDC构建高效数据管道解决方案

随着企业数据规模的快速增长和多样化的数据&#xff0c;如何高效地捕获、同步和处理数据成为了业务发展的关键。本文将介绍如何利用ETLCloud CDC技术&#xff0c;构建一套高效的CDC数据管道&#xff0c;实现实时数据同步和分析&#xff0c;助力企业实现数据驱动的业务发展。 一…

SpringBoot security 安全认证(一)——登录验证

本节内容&#xff1a;使用springboot自动security模块实现用户登录验证功能&#xff1b; 登录过程如下图&#xff1a; AuthenticationManager内容实现用户账号密码验证&#xff0c;还可以对用户状态&#xff08;启用/禁用&#xff09;&#xff0c;逻辑删除&#xff0c;账号是否…

IPSec VPN与NQA联动实现主备对等体和主备链路快速切换案例

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系&#xff01; 厦门微思网络​​​​​​https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom Linux\RHCE\RHCE 9.0\RHCA\ Oracle OC…

java设计模式:策略模式

在平常的开发工作中&#xff0c;经常会用到不同的设计模式&#xff0c;合理的使用设计模式&#xff0c;可以提高开发效率&#xff0c;提高代码质量&#xff0c;提高代码的可拓展性和维护性。今天来聊聊策略模式。 策略模式是一种行为型设计模式&#xff0c;运行时可以根据需求动…

记录 arm 开发板上 nginx 配置 http 服务注意事项

1. 自定义项目&#xff0c;需要在 conf.d 目录中增加一个 .conf 配置文件&#xff1a; server {listen 9200; # 端口号server_name localhost; # 服务名称location / {root /home/imx6q/media; # 项目根目录&#xff08;需要修改 n…

redis布隆过滤器(Bloom)详细使用教程

文章目录 布隆过滤器1. 原理2. 结构和操作3. 特点和应用场景4. 缺点和注意事项 应用-redis插件布隆过滤器使用详细过程安装以及配置springboot项目使用redis布隆过滤器下面是布隆过滤器的一些基础命令 扩展 布隆过滤器 Bloom 过滤器是一种概率型数据结构&#xff0c;用于快速判…

AcWing算法学习笔记:贪心(区间问题 + Huffman树 + 排序不等式 + 绝对值不等式 + 推公式)

贪心 一、区间问题①区间选点②最大不相交区间数量③区间分组④区间覆盖 二、Huffman树&#xff08;合并果子&#xff09;三、排序不等式&#xff08;排队打水&#xff09;四、绝对值不等式&#xff08;货仓选址&#xff09;五、推公式&#xff08;耍杂技的牛&#xff09; 一、…

常见API

文章目录 Math类1.1 概述1.2 常见方法 System类2.1 概述2.2 常见方法 Runtime3.1 概述3.2 常见方法 Object类4.1 概述4.2 常见方法 Objects类5.1 概述5.2 常见方法 BigInteger类6.1 引入6.2 概述6.3 常见方法6.4 底层存储方式&#xff1a; 7 BigDecimal类7.1 引入7.2 概述7.3 常…

leetcode 1.两数之和(C++)DAY1(待补充哈希表法)

文章目录 1.题目描述示例提示 2.解答思路3.实现代码结果4.总结 1.题目描述 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&…

猫用空气净化器真的能除菌吗?除毛可以用宠物空气净化器吗?

猫咪给我们带来了无尽的欢乐&#xff0c;但它们换毛时家里到处都是猫毛。我们会在地板、沙发上发现一大堆&#xff0c;甚至衣服也难逃其影响。这些浮毛中可能携带着微生物和尘螨等。对于免疫力较低的老年人、孩子和孕妇来说&#xff0c;他们更容易感染这些微生物。而对于鼻炎患…

openlayers加载天地图

申请天地图key 官方&#xff1a;https://www.tianditu.gov.cn/ 申请key&#xff1a;https://sso.tianditu.gov.cn/login?servicehttps%3A%2F%2Fconsole.tianditu.gov.cn%2F 进去之后&#xff0c;先登录&#xff0c;如果没账号先注册一个就行。 可以创建个应用&#xff0c;…

Unity制作随风摇摆的植物

今天记录一下如何实现随风摇摆的植物&#xff0c;之前项目里面的植物摇摆实现是使用骨骼动画实现的&#xff0c;这种方式太消耗性能&#xff0c;植物这种东西没必要&#xff0c;直接使用顶点动画即可。 准备 植物不需要使用标准的PBR流程&#xff0c;基础的颜色贴图加上法向贴…

153基于matlab的滚动轴承故障诊断

基于matlab的滚动轴承故障诊断&#xff0c;基于小波包分解&#xff0c;得到数据峭度值&#xff0c;以正常与故障数据峭度差值进行最大尺度重构&#xff0c;对重构信号进行包络谱分析。程序已调通&#xff0c;可直接运行。 153matlab 信号重构 包络谱分析 故障诊断 (xiaohongshu…

网络流数据集处理(深度学习数据处理基础)

一、数据集处理 处理数据集是一个文件夹 一个文件夹处理的&#xff0c;将原网络流数据集 放入一个文件夹 处理转换成 Json文件。&#xff08;数据预处理&#xff09;然后将这些文件处理成目标文件格式 再分割成训练集和测试集。每次运行只会处理一个文件夹。 运行train.py 导入…