数位dp(模板)

article/2025/9/13 13:00:31

数位dp问题题型往往是这样的:

给定一个区间[L,R],求这个区间中满足“某种条件”的数的总数。

题目:求区间[L,R]范围内有多少带3的数,所谓带3的数就是这个数十进制表示中存在至少一位3

比如3,,123,3333,都是带3的数,如12,456,1000都是不带3的数

输入:一行包含两个整数L,R(1<=L<R<1e12)

输出:输出一个整数表示区间[L,R]范围内带3的个数

例子:输入 100 2000

输出:19

在数位dp中由于数字过大,基本都要使用记忆化搜索,我们通过分析可以知道,在dfs中很多的数和前面dfs算过的数是一样的,只要不在限制位,我们把它存起来直接用就行了

 

#include<iostream>
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define maxn 15
int num;
int* a = new int[maxn];
int f[15];
//int a[maxn];
int b[maxn];//b保存第p为存的是那个数
int ten[maxn];
int L, R;
int t;
int dfs(int p, bool limit) {//p表示在第p位,limite表示此时是否处于限制位if (p < 0) {//for (int i = 2; i >= 0; i--)cout << b[i];//无限递归,记得加结束return//cout << endl;return 0;//搜索结束,返回}if (!limit && f[p] != -1) {//记忆化搜索,不处于限制位,并且f[p]被算过了return f[p];}int up = limit ? a[p] : 9;//判断是否处于限制位,如果是就只能取到a[p]为,否则最高位能取到9int ans = 0;for (int i = 0; i <= up; i++) {//b[p] = i;if (i == 3) {if (limit && i == up) {ans += 1;for (int j = p - 1; j >= 0; j--)//处于限制条件就把限制数下面全算上ans += a[j] * ten[j];}else//如果不处于限制条件直接加上10的p次方ans += ten[p];}else ans += dfs(p - 1, limit && i == up);//这里填a[p]可以填up也行,在处于限制的时候up等于a[p]}if (!limit)//记忆化搜索,如果没有处于限制条件就可以直接那算过一次的数直接用,能节省很多时间f[p] = ans;return ans;
}int handle(int num) {int p = 0;while (num) {//把num中的每一位放入数组a[p++] = num % 10;num /= 10;}//说明a数组写进去了,但是读取无效数据是什么意思勒,之前好像不是这样的,解决办法,动态创建数组/*for (int i = 0; i < p; i++) {cout << a[i];}*/return dfs(p - 1, true);//对应的最高位为p-1位,为True表示没有处于限制位
}void init() {ten[0] = 1;for (int i = 1; i < 15; i++) {ten[i] = ten[i - 1] * 10;}memset(f, -1, sizeof(f));
}
int32_t  main() {cin>>t;while(t--){cin>>L>>R;//handle(23);init();//一定要记得初始化,TM的我在这卡了半个月cout << handle(R)-handle(L) << endl;delete[]a;}return 0;
}

如果要输出不带3 的数只要把上面的代码的函数修改一下就好了

dfs函数修改成这样:如果是带3的数就直接continue掉,而把不带3 的数加起来

int dfs(int p, bool limit) {//p表示在第p位,limite表示此时是否处于限制位//求不带3的个数if (p < 0)return 1;//说明位数全递归了if (!limit && f[p] != -1)return f[p];//记忆化搜索,直接返回算过的值,减少计算时间int up = limit ? a[p] : 9;int ans = 0;for (int i = 0; i <= up; i++) {if (i == 3)continue;ans += dfs(p - 1, limit && i == up);}if (!limit)f[p] = ans;//记忆化搜索return ans;
}

handle函数修改:因为我们还要用到num所以num的值不能改变,要定义另外一个num来计算num的每一位数:

int handle(int num) {int p = 0;int x = num;while (x) {//把num中的每一位放入数组a[p++] = x % 10;x /= 10;}//说明a数组写进去了,但是读取无效数据是什么意思勒,之前好像不是这样的,解决办法,动态创建数组/*for (int i = 0; i < p; i++) {cout << a[i];}*/return num + 1 - dfs(p - 1, true);//对应的最高位为p-1位,为True表示没有处于限制位//在0到num中有num+1个数
}

 下面就是算不带3的数的所有代码了:

#include<iostream>
using namespace std;
#define int long long
#define maxn 15
int num;
int* a = new int[maxn];
int f[15];
//int a[maxn];
int b[maxn];//b保存第p为存的是那个数
int ten[maxn];
int L, R;
int t;
int dfs(int p, bool limit) {//p表示在第p位,limite表示此时是否处于限制位//求不带3的个数if (p < 0)return 1;//说明位数全递归了if (!limit && f[p] != -1)return f[p];//记忆化搜索,直接返回算过的值,减少计算时间int up = limit ? a[p] : 9;int ans = 0;for (int i = 0; i <= up; i++) {if (i == 3)continue;ans += dfs(p - 1, limit && i == up);}if (!limit)f[p] = ans;//记忆化搜索return ans;
}int handle(int num) {int p = 0;int x = num;while (x) {//把num中的每一位放入数组a[p++] = x % 10;x /= 10;}//说明a数组写进去了,但是读取无效数据是什么意思勒,之前好像不是这样的,解决办法,动态创建数组/*for (int i = 0; i < p; i++) {cout << a[i];}*/return num + 1 - dfs(p - 1, true);//对应的最高位为p-1位,为True表示没有处于限制位//在0到num中有num+1个数
}void init() {ten[0] = 1;for (int i = 1; i < 15; i++) {ten[i] = ten[i - 1] * 10;}memset(f, -1, sizeof(f));
}
int32_t  main() {cin >> t;while (t--) {cin >> L >> R;//handle(23);init();//一定要记得初始化,TM的我在这卡了半个月cout << handle(200) - handle(100) << endl;delete[]a;}return 0;
}

杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
不吉利的数字为所有含有4或62的号码。例如:
62315 73418 88914
都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。
你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。

Input

输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。

Output

对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。

Sample Input

1 100
0 0

Sample Output

80

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#define LL long long
using namespace std;
int digit[10];
LL dp[10][2];
LL dfs(int len, int if6, int limit)
{if (len == 0) return 1; //数到第0位返回1,因为0符合条件if (!limit && dp[len][if6]) return dp[len][if6];//如果不是上限,则不用重新数,直接返回,记忆化搜索的体现LL ans = 0, up;if (limit)  up = digit[len];else up = 9;for (int i = 0; i <= up; i++){if (if6 && i == 2) continue;if (i == 4) continue;ans += dfs(len - 1, i == 6, limit && i == digit[len]);//  limit&&i==up也可以}if (!limit) dp[len][if6] = ans;//记忆化搜索return ans;
}
LL cal(LL x)
{int len = 0;while (x){digit[++len] = x % 10;x /= 10;}return dfs(len, 0, 1);
}
int main()
{int n, m;while (~scanf("%d%d", &n, &m)){if (!n && !m)break;// printf("%lld  %lld\n",cal(m),cal(n));printf("%lld\n", cal(m) - cal(n - 1));}return 0;
}

下面这个一样是不带49的数

题目:求区间[1,n]范围内包含带49的数。

一个数是带49的数,当且仅当他的十进制标示值中存在连续的两位,其中高位为4,较低位为9,比如49,149,1234987等;而4,12345,94,9999444都不是

输入格式:输入一个整数n(1<=n<=2^63)

输出格式:输出一个整数表示区间[1,n]范围内存在多少带49的数

输出样例:

输入:500 

输出:15

#include<iostream>
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[22], f[22][10];//后面这个10用于确定前面那个数是哪一个
int n;
int t;
int dfs(int p, int pre, bool limit) {//p表示第p位数,pre表示p+1位数if (p < 0) {return 1;}if (!limit && f[p][pre] != -1) {return f[p][pre];}int up = limit ? a[p] : 9;int ans = 0;for (int i = 0; i <= up; i++) {if (pre == 4 && i == 9) {continue;}ans += dfs(p - 1, i, limit && i == up);}if (!limit)f[p][pre] = ans;return ans;
}
int handle(int num) {int p = 0;int x = num;while (x) {a[p++] = x % 10;x /= 10;}return num + 1 - dfs(p - 1, 0, true);
}void init() {memset(f, -1, sizeof(f));}
int32_t main() {cin >> t;while (t--) {cin >> n;init();cout << handle(n) << endl;}return 0;
}


http://chatgpt.dhexx.cn/article/5CjUxD2l.shtml

相关文章

数位DP讲解

转载自&#xff1a;http://www.cnblogs.com/itlqs/p/5935308.html 数位DP其实是很灵活的&#xff0c;所以一定不要奢求一篇文章就会遍所有数位DP的题&#xff0c;这一篇只能是讲清楚一种情况&#xff0c;其他情况遇到再总结&#xff0c;在不断总结中慢慢体会这个思想&#xff0…

数位dp入门详解

基础篇 数位dp是一种计数用的dp&#xff0c;一般就是要统计一个区间[le,ri]内满足一些条件数的个数。所谓数位dp&#xff0c;字面意思就是在数位上进行dp咯。数位还算是比较好听的名字&#xff0c;数位的含义&#xff1a;一个数有个位、十位、百位、千位......数的每一位就是数…

数位dp。

一&#xff0c;思想&#xff1a; 在处理1e9甚至1e18,1e100的问题时&#xff0c;因为在统计情况下有很多重复的计算&#xff0c;数位dp实现了相同状态只计算一次&#xff0c;从而大幅减少运算时间&#xff0c;思想就是对每一位进行dp&#xff0c;计算时记忆化每一位可以有的状态…

【进阶】数位DP详解

如果想了解更多内容&#xff0c;欢迎关注我的微信公众号&#xff1a;信息学竞赛从入门到巅峰。 戳这里获得更好的阅读体验哦 https://mp.weixin.qq.com/s/eZHoI7RZOvlEhhSNRpGhxA 今天&#xff0c;我向大家介绍一种特殊的DP类型——数位DP。 数位DP这类题目一般不会出现在提高…

超详细讲解数位DP

什么是数位dp 数位dp是一种计数用的dp&#xff0c;一般是要统计一个区级[l,r]内满足一些条件的数的个数 所谓数位dp&#xff0c;就是对数位进行dp&#xff0c;也就是个位、十位等 相对于普通的暴力枚举&#xff0c;数位dp快就快在它的记忆化&#xff0c;也就是说后面可能会利…

数位DP,看这一篇就足够了!

数位DP用来解决什么问题&#xff1f; 我们有时候会遇到这样一类题目&#xff0c;给你一个区间 [l,r] &#xff0c;找区间上符合某种特定要求的数的个数&#xff0c;这个要求可能很简单&#xff0c;很好理解&#xff0c;但是由于区间范围太大&#xff0c;以至于对每个数进行遍历…

数位dp介绍

不了解dp的可以先看一下dp 数位dp含义&#xff1a; 数位&#xff1a;一个数有个位&#xff0c;十位&#xff0c;百位&#xff0c;千位等等&#xff0c;数的每一位都是数位。 数位dp归为计数dp&#xff0c;是在数位上进行操作的dp。 数位dp的实质是一种快速枚举的方式&#xff0…

动态规划——数位dp

数位dp 文章目录 数位dp概述题目特征基本原理计数技巧 模板例题度的数量思路代码 数字游戏思路代码 不要62思路代码 概述 数位是指把一个数字按照个、十、百、千等等一位一位地拆开&#xff0c;关注它每一位上的数字。如果拆的是十进制数&#xff0c;那么每一位数字都是 0~9&am…

数位DP学习整理(数位DP看完这篇你就会了)

文章目录 数位DP数位DP介绍数位DP解法数位DP经典例题例题1&#xff1a;度的数量例题2&#xff1a;计数问题例题3&#xff1a;数字游戏例题4&#xff1a;windy数例题5&#xff1a;数字游戏Ⅱ例题6&#xff1a;不要62例题7&#xff1a;恨7不成妻 数位DP总结 数位DP 数位DP介绍 …

Mysql悲观锁和乐观锁区别

1、mysql悲观锁&#xff1a;在整个数据处理过程中&#xff0c;将数据处于锁定状态。悲观锁的实现&#xff0c;依靠数据库提供的锁机制&#xff0c;每次会申请锁并加锁和解锁操作 第一步&#xff1a;两个终端均关闭自动提交 左边&#xff1a; 右边&#xff1a; 第二步&#xff1…

java的乐观锁和悲观锁

参考&#xff1a; https://www.cnblogs.com/jyroy/p/11365935.html https://www.jianshu.com/p/ae25eb3cfb5d 乐观锁和悲观锁 乐观锁和悲观锁是一种广义上的概念&#xff0c;体现了看待线程同步的不同角度。 乐观锁&#xff1a;对于并发操作产生的线程安全问题持乐观态度&…

MySQL中悲观锁和乐观区别

1、概念不同 乐观锁( Optimistic Locking)&#xff1a; 顾名思义&#xff0c;对加锁持有一种乐观的态度&#xff0c;即先进行业务操作&#xff0c;不到最后一步不进行加锁&#xff0c;"乐观"的认为加锁一定会成功的&#xff0c;在最后一步更新数据的时候再进行加锁…

PyQt5 - 双QTimer实现并行输出

QTime的使用 双Qtime的实现原理 一&#xff1a;QTime的使用 # -*- coding: utf-8 -*-# Form implementation generated from reading ui file D:\Qt\QT-Projects\UI项目\时间实时更新.ui # # Created by: PyQt5 UI code generator 5.12.2 # # WARNING! All changes made in t…

QTimer 定时器

QTimer类为我们提供了一个即可重复触发又可单次触发的定时器。它是一个高层次的应用程序接口。要使用它&#xff0c;只需创建一个QTimer类对象&#xff0c;将它的timeout&#xff08;&#xff09;信号连接到适当的函数上&#xff0c;然后调用其start&#xff08;&#xff09;函…

QTimer使用

QTimer工作流程 程序示例 test_timee.h //继承QObject&#xff0c;使用信号/槽 class TestTimer: public QObject {Q_OBJECT public:TestTimer(QObject *parent nullptr);public slots:void start();void stop();void do1();private://定义timer对象QTimer m_timer; }; tes…

定时器QTimer

学习PyQt推荐大家看这本书&#xff1a;https://weread.qq.com/web/reader/6393267071ccfa97639f573 链接&#xff1a;https://pan.baidu.com/s/1ZuHxNvEYtUqzSWqytN7viw 提取码&#xff1a;qku8 import sys from PyQt5.QtWidgets import QApplication,QWidget from PyQt5 i…

Qt QTimer定时器

1.QTimer简介 QTimer 主要的属性是 interval&#xff0c;是定时中断的周期&#xff0c;单位毫秒。QTimer 主要的信号是 timeout()&#xff0c;在定时中断时发射此信号&#xff0c;要想在定时中断里做出响应&#xff0c;这就需要编写 timeout() 信号的槽函数。 2.常用API //设…

Qt定时器QTimer使用教程与代码演示

Qt提供了定时器类QTimer, 在使用时需要包含头文件 #include <QTimer>QTimer类方法介绍: void start(int msec); 开启定时器&#xff0c;定时间隔的msec毫秒void stop(); 结束定时 QTimer信号&#xff1a; void timeout(QPrivateSignal); 在链接定时器时&#xff0c;需…

【Qt】QTimer的简单使用

定义定时器对象&#xff1a;QTimer *myTimer; 动态分部内存空间&#xff1a;myTimer new QTimer(this); 启动定时器&#xff1a;myTimer->start(100); 定时器超时事件&#xff1a;QTimer::timeout() 停止定时器&#xff1a;myTimer->stop(); 等等&#xff1b; 程序实现功…

QT之QTimer详解以及结合多线程中开启定时器的示例

一 QTimer详解 QTimer类提供了重复和单次触发信号的定时器。 a.void timeout ()定时器超时后&#xff0c;这个信号被发射。 b.void start()开启定时器,它的重载函数void start(int msec),启动或重新启动一个超时时间间隔为毫秒的定时器,如果定时器正在运行&#xff0c;它将被停…