【leetcode 2719.统计整数数目】特殊动态规划之数位DP(数位动态规划)

2719. 统计整数数目

题目描述

给你两个数字字符串 num1 和 num2 ,以及两个整数 max_sum 和 min_sum 。如果一个整数 x 满足以下条件,我们称它是一个好整数:

  • num1 <= x <= num2
  • min_sum <= digit_sum(x) <= max_sum.

请你返回好整数的数目。答案可能很大,请返回答案对  1 0 9 + 7 10^9 + 7 109+7 取余后的结果。

注意,digit_sum(x) 表示 x 各位数字之和。

  • 1 ≤ n u m 1 ≤ n u m 2 ≤ 1 0 22 1 \le num_1 \le num_2 \le 10^{22} 1num1num21022
  • 1 <= min_sum <= max_sum <= 400

思路

最直接的思路就是遍历[num1,num2]的所有整数,判断其数位之和是否满足条件。

for(int i = num1;i <= num2;++i) {if(digit_sum(i) >= min_sum && digit_sum(i) <= min_sum) {++res;}
}

对于一个“困难”题目来说,这个思路无疑太简单了,提交后果然会超时。

数位DP

那么除此之外还有什么解决方案吗?这就要涉及到数位DP了,所谓数位DP:

数位是指把一个数字按照个、十、百、千等等一位一位地拆开,关注它每一位上的数字。如果拆的是十进制数,那么每一位数字都是 0~9,其他进制可类比十进制。

数位 DP:用来解决一类特定问题,这种问题比较好辨认,一般具有这几个特征:

  1. 要求统计满足一定条件的数的数量(即,最终目的为计数);
  2. 这些条件经过转化后可以使用「数位」的思想去理解和判断;
  3. 输入会提供一个数字区间(有时也只提供上界)来作为统计的限制;
  4. 上界很大(比如 1 0 18 10^{18} 1018),暴力枚举验证会超时。

数位DP的详细讲解可以参考OI Wiki 数位DP

按照上述特征,本题目明显属于数位DP能够解决的问题:

  1. 本题目要求统计满足 m i n _ s u m ≤ d i g i t _ s u m ( n ) ≤ m a x _ s u m min\_sum \le digit\_sum(n) \le max\_sum min_sumdigit_sum(n)max_sum的整数数量
  2. 数位和本身就须需要使用”数位“的思想去理解和解决
  3. 输入提供了上界和下界
  4. 上界很大,达到 1 0 22 10^{22} 1022,导致普通思路必然超时。

所以本题目是一种典型的数位DP问题,现在我们可以利用数位DP的方法来解决本题目了。

基于数位DP的解决方案

假设对于一个长度为l的数字num,我们定义f(string num, int i, int j, bool limit)表示构造第i位及其之后数位满足要求的方案数目,另外:

  • j表示前面选中数字(0到i - 1)的数字和。
  • limit,表示当前是否受到了n的约束,因为选择数字时,数字构成的数不能大于n。比如n为123,如果第2位选择2,此时第3位要受到n的限制,只能选择1、2或者3;而如果第2位选择1,此时第3位是不受到限制的,可以选择0-9中的任意数字

那么f(num, 0, 0, true),也就是第0位及其之后数位满足要求的方案数目,也就是区间[0, num]中所有整数中满足数位和大于等于min_sum,并小于等于max_sum的个数。

那么为了求解区间[num1,num2]中所有满足要求的整数个数,只需要f(num2,0,0,true) - f(num1 -1, 0,0,true),也就是最终答案。

在计算过程中,对于f(int i, int j, bool limit)

  • 如果j已经超过了max_sum的限制,表示已经不可能满足要求了,直接返回0。

  • 如果i == l,就是数位0到l-1都已经选定了,也就是已经构造完成,此时j如果满足要求(min_sum <= j <= max_sum),就构成一个方案,返回1.

  • 遍历当前位可能的选择:

    • 如果limit为true,此时需要受到n的限制,也就是只能选择0到n[i](n的第i位)
    • 如果limit为false,可以选择0到9

    对于每一种选择x,可能的方案数是f(i + 1, j + x, limit && x == n[i]),所有可能选择x对应的方案数加起来就是f(i,j,limit)的答案。

为了提升计算效率,我们利用dp[i][j]表示数字填写到第i位,已填的数字位数之和为j时,符合条件的数字个数。

C++代码实现如下:

class Solution {
private:const int MOD = 1e9 + 7;int max;int min;int dfs(string num, int i, int j, bool limit,vector<vector<int>>& dp) {if(j > this->max) {return 0;}if(i == num.size()) {return j >= this->min;}if(!limit && dp[i][j] != -1){return dp[i][j];}int res = 0;int up = limit ? num[i] - '0' : 9;for(int d = 0;d <= up;++d) {res = (res + dfs(num, i + 1, j + d, limit && d == up, dp)) % MOD;}if(!limit) {dp[i][j] = res;}return res;}int get(string num) {vector<vector<int>> dp = vector<vector<int>>(num.size(), vector<int>(this->max + 1, -1));return dfs(num, 0, 0, true, dp);}// 获取num - 1对应数字string strNumDes(string num) {int i = num.size() - 1;while (num[i] == '0') {i--;}num[i]--;i++;while (i < num.size()) {num[i] = '9';i++;}return num;}
public:int count(string num1, string num2, int min_sum, int max_sum) {this->max = max_sum;this->min = min_sum;return (get(num2) - get(strNumDes(num1)) + MOD) % MOD;}
};

看完代码实现后,读者可能存在一些疑问:

  • 为什么只有在limitfalse的情况下才使用缓存dp?

    设想一种情况,对于num = 321min_sum = 0max_sum = 12的情况:

    • 当遍历到23时,也就是i = 2, j = 5limit = false,此时记录了dp[2][5]
    • 等继续遍历到32时,此时也是i = 2j = 5,但是limit = true

    这两种情况下,虽然ij相同,但满足要求的方案数是不同的,前者有8种(230、231、232、233、234、235、236、237),后者受限于数字num,只有2种(320、321)。

    因此,只有在limit为false时才使用缓存。

  • 基于上面那个问题,那如果将limit加入到缓存维度中,是否就可以不再特殊考虑limit了?比如dp[2][i][j],其中dp[0][i][j]表示limit为false的情况,dp[1][i][j]表示limit为true的情况。

    这种方案当然可以得到最终答案,但在这么做之前可以先思考一下是否有必要。
    limit为true的子问题只会被调用一次,将对这些子问题做缓存并不会提升性能,所以并没有必要这么做。

其他数位DP问题

数位DP相关的题目有很多,但只要了解其中规律,应该还是可以顺利解决的,罗列下面几个数位DP相关题目,供大家训练加强。

  • 233. 数字 1 的个数
  • 面试题 17.06. 2出现的次数
  • 600. 不含连续1的非负整数
  • 902. 最大为 N 的数字组合
  • 1067. 范围内的数字计数
  • 1012. 至少有 1 位重复的数字

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

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

相关文章

微信小程序定义并获取日志/实时log信息

步骤一&#xff1a;开通实时日志 可以在开发者工具->详情->性能质量->实时日志&#xff0c;点击前往&#xff0c;在浏览器打开we分析界面&#xff1a; 也可登录小程序管理后台&#xff0c;点击统计进入we分析&#xff1a; 在we分析界面找到性能质量&#xff0c;打开实…

【提示学习论文七】Visual Prompt Tuning论文原理

文章目录 Visual Prompt Tuning&#xff08;VPT&#xff09;文章介绍Abstract1 Introduction2 Related Work3 Approach3.1 准备工作3.2 Visual-Prompt Tuning(VPT)3.2.1 VPT-Shallow3.2.2 VPT-Deep3.2.3 Storing Visual Prompts 存储视觉提示 4 实验主要结果模型设计变体的消融…

Python文本向量化入门

一、引言 文本向量化是将文本数据转换为数值型格式的过程&#xff0c;以便能够使用机器学习算法进行训练和预测。在Python中&#xff0c;文本向量化通常使用词袋模型&#xff08;Bag of Words&#xff09;或TF-IDF等统计方法来实现。本文将介绍如何使用Python进行文本向量化&a…

抠图换背景的工具有吗?分享4款好用的!

在数字时代&#xff0c;设计已经成为了我们生活中不可或缺的一部分。无论是为了工作还是个人爱好&#xff0c;我们都需要掌握一些设计技能。其中&#xff0c;抠图换背景是一项非常重要的技能。那么&#xff0c;有哪些工具可以帮助我们快速、准确地完成这项任务呢&#xff1f;今…

公司运营数据分析大屏:引领企业决策,驱动业务增长

在数字化时代&#xff0c;数据已经成为企业决策的关键。为了更好地洞察市场趋势、优化业务流程、提升运营效率&#xff0c;越来越多的企业开始引入数据分析大屏以分析公司运营状况。这一创新举措不仅改变了传统的管理模式&#xff0c;更引领企业迈向智能化决策的新篇章。 公司运…

什么是CDN(内容分发网络)

CDN通过在全球范围内分布的服务器网络优化数据传输&#xff0c;大幅提升了网站性能和用户体验。 将详细介绍CDN的工作原理、应用和它如何改变我们访问互联网内容的方式。 CDN的基本概念 定义&#xff1a;CDN是一组分布在多个地理位置的服务器网络&#xff0c;旨在通过更接近用…

VPN深度解析:构建安全网络的关键技术

文章目录 VPN&#xff08;虚拟私人网络&#xff09;简介VPN的工作原理VPN与DNS&#xff08;域名系统&#xff09;DNS的基本工作原理VPN中的DNS查询VPN与DNS泄露保护VPN与智能DNS VPN与DHCP&#xff08;动态主机配置协议&#xff09;DHCP的基本功能VPN环境中的DHCPVPN与DHCP的结…

Vue3+antDesignVue实现表单校验

一 <a-formref"form":model"form":rules"rules":label-col"{ md: { span: 6 }, sm: { span: 24 } }":wrapper-col"{ md: { span: 18 }, sm: { span: 24 } }"><!-- <a-form-item label创建人: namecreated…

MySQL进阶45讲【2】日志系统:一条SQL更新语句是如何执行的?

1 前言 上篇文章我们系统了解了一个查询语句的执行流程&#xff0c;并介绍了执行过程中涉及的处理模块。相信大家还记得&#xff0c;一条查询语句的执行过程一般是经过连接器、分析器、优化器、执行器等功能模块&#xff0c;最后到达存储引擎。 那么&#xff0c;一条更新语句…

最新使用宝塔反代openai官方API接口搭建详细教程及502 Bad Gateway错误问题解决

一、前言 宝塔反代openai官方API接口详细教程&#xff0c;实现国内使用ChatGPT502 Bad Gateway问题解决&#xff0c; 此方法最简单快捷&#xff0c;没有复杂步骤&#xff0c;不容易出错&#xff0c;即最简单&#xff0c;零代码、零部署的方法。 二、实现前提 一台海外VPS服务…

vite打包相关+本地http-server运行打包dist文件进行检测

目录 一.去到vite.config.ts文件 1.添加内容 2.解释 3.打包 二.本地开启http-server服务 1.全局安装http-server 1.1可以通过如下命令查看是否安装http-server 1.2使用如下命令安装 2.进入项目启动服务 3.查看效果 一.去到vite.config.ts文件 1.添加内容 build: {o…

浪之潮科技:动力恢复清积碳,尾气治理三元催化修复

针对汽车出现油耗增加、动力减弱以及尾气检测不合格等情况&#xff0c;深圳市浪之潮科技有限公司&#xff08;以下简称&#xff1a;浪之潮科技&#xff09;求真务实、勇于创新&#xff0c;独创两大系统六大部位——动力恢复清积碳、尾气治理三元催化修复&#xff0c;为广大车主…

【iOS】数据持久化(四)之FMDB基本使用

正如我们前面所看到的&#xff0c;原生SQLite API在使用时还是比较麻烦的&#xff0c;于是&#xff0c;开源社区就出现了一系列将SQLite API进行封装的库&#xff0c;其中FMDB的被大多数人所使用 FMDB和SQLite相比较&#xff0c;SQLite比较原始&#xff0c;操作比较复杂&#…

swagger标签说明

x-ref-external可以为yaml里的数据结构指定一个已有的数据类&#xff0c;例如&#xff1a; PageVO:x-ref-external: com.lee.PageVOproperties:totalRows:type: stringdescription: 总条数curPage:type: stringdescription: 当前页pageSize:type: stringdescription: 页大小指…

进程切换和是Linux2.6内核中进程调度的算法

正文开始前给大家推荐个网站&#xff0c;前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 进程切换 进程并发就需要做到进程切换&#xff0c;一个CPU一套寄存器但是需要运行的进程有很多…

基于cy7c68013的逻辑分析仪nanoDLA全套软件linux下编译测试

0. 环境 - win10 - ubuntu22 - nanoDLA 提前获取到源码&#xff1a;-> 浏览器打开 https://github.com/wuxx/nanoDLA -> Download as zip. 硬件就直接用taobao买到的&#xff0c;原理图是 1. win10出厂测试 1.1 安装pulseview nanoDLA-master\software\pulseview-0.4.…

014集:python访问互联网:网络爬虫实例—python基础入门实例

以pycharm环境为例&#xff1a; 首先需要安装各种库(urllib&#xff1a;requests&#xff1a;Openssl-python等) python爬虫中需要用到的库&#xff0c;大致可分为&#xff1a;1、实现 HTTP 请求操作的请求库&#xff1b;2、从网页中提取信息的解析库&#xff1b;3、Python与…

外贸货源怎么找?9大优质货源渠道分享!

近几年跨境电商无货源模式大火了一把&#xff0c;让不少人都跃跃欲试。毕竟这种模式投资少&#xff0c;门槛低&#xff0c;回本快&#xff0c;想增加额外收入或创业的人们都争相涌入。但是要想做得好&#xff0c;选好货源渠道就是关键了。如果不小心选错了供应商&#xff0c;可…

关键词提取

在自然语言处理领域中&#xff0c;处理海量文本信息的关键在于把用户关心的问题提取出来。而关键词是能够表达文档中心内容的词语&#xff0c;更是表达文档主题的最小单位。因此&#xff0c;文本关键词的提取对于文本信息的理解是至关重要的。 关键词提取是文本挖掘领域下的一个…

javacv和opencv对图文视频编辑-java项目搭建1

要搭建javacv项目&#xff0c;你需要按照以下步骤进行操作&#xff1a; 下载并安装OpenCV库&#xff1a;访问OpenCV的官方网站&#xff08;https://opencv.org/&#xff09;并下载适合你系统的版本。安装完成后&#xff0c;将OpenCV的库文件添加到你的项目中。 添加JavaCV库&a…