数位DP详解
算法使用范围
在一个区间里面求有多少个满足题目所给的约束条件的数,约束条件必须与数自身的属性有关
下面用kuangbin数位dp的题来介绍
例题 不要62
题意:在一个区间里面求出有多少个不含4和62的数
做法:平常我们的做法肯定是从L枚举到R,然后数位上一个一个的判断,但是如果是范围过大的话我们不可以预处理存储,只能每次去枚举,这样肯定会超时
所以我们使用数位DP 记忆化搜索,我们dfs去枚举每一位
数位部分
例如 2567
因为千位是2的时候我的百位只能从0-5,1的时候百位却是可以0-9,所以这点我们需要判断
第一位 0-2
第二位 第一位==2?5:9
第三位 第一位==5&&第二位==6?6:9
第四位 第一位==5&&第二位==6&&第三位==6&&第四位==7?7:9
上面也就是数位的过程,数位我们就省去了每一个数去拆分查询的复杂度,大概是复杂度/10
dfs参数
虽然参数比较灵活,但是有几个固定的参数,做多了数位DP的题也会觉得这个参数其实是一个套路了
第一个参数
从上述的的枚举,我们可以看出我们首先要把这个数拆分到数组里面,然后dfs每位每位的枚举
ans 代表的是当前枚举到(个,十,百,千...)位了
第二个参数
从上述来看,每到一位我都需要知道前面是不是上边界的值,从而来确定我的当前位的枚举范围
flag 代表的数前一位是否还是处于上边界
第三个参数
iszero 用来判断前导0,有些题目会有前导零的要求,比如说求二进制的数位的时候他前面
第四个参数
issix 比如说这题的话,那么我就要判断前面一位是否是6,那么当前位是2的话那么我就可以忽略了,这个参数要灵活变通
dp部分
dp的主要目的就是尽量把大部分重复问题的东西通过巧妙的存储保留下来,下次可以直接访问,大大减少复杂度
dp[ans][issix]
数组的用途解析(注意!!!)
ans代表剩余的位数,issix代表是否是6
注意的是我们必须保存的是和数的属性相关的东西,比如求数不包括62的,我们求1-1000和1-10000以内和数自己都是没关系的,他每次依然会被计数
现在我知道 1-1000的62的个数 说明这是前面是没有6的情况
我要求42000-50000之间有多少个
我们就会发现其实42000-43000其实和1-1000其实是一样的,43000-50000也是一样的道理,所以我们可以用,这是前缀没有6的情况 dp[ans][0]
我要求60000-63000之间有多少个
因为前面有6了,所以我们这个我们最开始用不到1-1000内62的个数这个条件,我们跑出60000-61000的结果时
我们就相当于知道了dp[ans][1]的值,后面61000-63000我们都可以使用
因为有前面是6和不是6的情况,所以我们列要开两种情况
列标这里的作用比较灵活
比如我们要求这个数的数位和能否被10整除
那么我们就要开10,分别对应前缀%10对后面的影响
总之,前缀提供啥,后面要根据前缀的情况来给出不同情况的值,前缀必须没有后效性,前缀不用知道后缀就可以满足情况
小优化
有些题它可能是多组输入,我们就可以把memset(dp,-1,sizeof(dp))放多组输入外面
回到开始说的必须是数的属性,永远不会变,所以我们计算后的数组可以保留到下一组数据使用
当然如果是把上列说的%10改成输入%q那就不行了
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[][];
int a[];
int sum=;
int dfs(int len,bool issix,bool flag)//len代表当前枚举的位,issix代表前面是否是6,flag代表是否还是处于上边界
{
if(len==-) return ;//依题意来决定是1还是0,这题只是计算没有62的,所以我们直接返回1,如果题目是数位和是否%10==0就不一样了
int up=flag?a[len]:;//决定你枚举的上界是多少
if(!flag&&dp[len][issix]!=-) return dp[len][issix];//如果不是处于上边界并且之前求过了值的话可以直接返回
int tmp=;
for(int i=;i<=up;i++)
{
if(i==) continue;
if(issix&&i==) continue;
tmp+=dfs(len-,i==,flag&&i==a[len]);//上次是上边界,如果这次也是的话,那么下一位也是
}
if(!flag) dp[len][issix]=tmp;//求了值用dp存下,下次可以使用
return tmp;
}
int suan(int x)
{
int pos=;
while(x)
{
a[pos++]=x%;
x/=;
}
return dfs(pos-,,true);
}
int main()
{
int l,r;
memset(dp,-,sizeof(dp));
while(scanf("%d%d",&l,&r)!=EOF)
{
if(l==&&r==) break;
printf("%d\n",suan(r)-suan(l-));//利用前缀和来求固定区间的个数
}
}
这里讲一下为什么都是!flag的时候来进行操作
首先看这句
if(!flag&&dp[len][issix]!=-1) return dp[len][issix];
因为如果我现在知道了1-1000的个数
我现在要求2100-2500
那么求到2???的时候需不需要返回呢,答案是不行,
因为你返回的值里面还包括了 2500-3000的值,所以需要加!flag
再看这句
if(!flag) dp[len][issix]=tmp;
同样如果我求了到了2000-2500的值的时候我需不要把他当1-1000的值进行存储呢
也不行,因为你烧了2500-3000这一段没算
最后再推荐一篇大牛的博客
https://blog.csdn.net/jk211766/article/details/81474632
数位DP详解的更多相关文章
- 数位DP 详解
序 天堂在左,战士向右 引言 数位DP在竞赛中的出现几率极低,但是如果不会数位DP,一旦考到就只能暴力骗分. 以下是数位DP详解,涉及到的例题有: [HDU2089]不要62 [HDU3652]B-n ...
- 动态规划晋级——HDU 3555 Bomb【数位DP详解】
转载请注明出处:http://blog.csdn.net/a1dark 分析:初学数位DP完全搞不懂.很多时候都是自己花大量时间去找规律.记得上次网络赛有道数位DP.硬是找规律给A了.那时候完全不知数 ...
- 数位dp详解&&LG P2602 [ZJOI2010]数字计数
数位dp,适用于解决一类求x~y之间有多少个符合要求的数或者其他. 例题 题目描述 杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除 ...
- 状压DP详解(位运算)
前言: 状压DP是一种非常暴力的做法(有一些可以排除某些状态的除外),例如dp[S][v]中,S可以代表已经访问过的顶点的集合,v可以代表当前所在的顶点为v.S代表的就是一种状态(二进制表示),比如 ...
- 状态压缩dp 状压dp 详解
说到状压dp,一般和二进制少不了关系(还常和博弈论结合起来考,这个坑我挖了还没填qwq),二进制是个好东西啊,所以二进制的各种运算是前置知识,不了解的话走下面链接进百度百科 https://baike ...
- 线性DP详解
顾名思义,线性DP就是在一条线上进行DP,这里举一些典型的例子. LIS问题(最长上升子序列问题) 题目 给定一个长度为N的序列A,求最长的数值单调递增的子序列的长度. 上升子序列B可表示为B={Ak ...
- 状态压缩动态规划(状压DP)详解
0 引子 不要999,也不要888,只要288,只要288,状压DP带回家.你买不了上当,买不了欺骗.它可以当搜索,也可以卡常数,还可以装B,方式多样,随心搭配,自由多变,一定符合你的口味! 在计算机 ...
- 状压DP详解+题目
介绍 状压dp其实就是将状态压缩成2进制来保存 其特征就是看起来有点像搜索,每个格子的状态只有1或0 ,是另一类非常典型的动态规划 举个例子:有一个大小为n*n的农田,我们可以在任意处种田,现在来描述 ...
- 树形DP详解+题目
关于树形dp 我觉得他和线性dp差不多 总结 最近写了好多树形dp+树形结构的题目,这些题目变化多样能与多种算法结合,但还是有好多规律可以找的. 先说总的规律吧! 一般来说树形dp在设状态转移方程时都 ...
随机推荐
- POJ-3693/HDU-2459 Maximum repetition substring 最多重复次数的子串(需要输出具体子串,按字典序)
http://acm.hdu.edu.cn/showproblem.php?pid=2459 之前hihocoder那题可以算出最多重复次数,但是没有输出子串.一开始以为只要基于那个,每次更新答案的时 ...
- 他将Yahoo!Hadoop从20个节点扩展为42000个节点
他将Yahoo!Hadoop从20个节点扩展为42000个节点 http://www.csdn.net/article/2012-11-08/2811629-Interview-Hortonworks ...
- 20165309 实验三 敏捷开发与XP实践
2017-2018-2 20165309实验三<Java面向对象程序设计>实验报告 一.实验内容 1. XP基础 2. XP核心实践 3. 相关工具 二.实验步骤 (一)代码规范 1.在I ...
- 在word中粘贴的图片为什么显示不完整
一.背景 整理系统测试说明文档,截得图片粘贴到word中显示不完整. 二.错误问题 问题:在word中粘贴的图片为什么显示不完整,如图所示: 三.分析问题: 原因是原来设置的行间距是固定值,图片也作一 ...
- 【Oracle】【5】主键、外键管理
前言: 1,事实上我是不使用外键的,所以本文只介绍主键 正文: (1)创建表的同时创建主键约束 create table STUDENT ( ID int , NAME varchar(8), AGE ...
- leetcode-algorithms-12 Integer to Roman
leetcode-algorithms-12 Integer to Roman Roman numerals are represented by seven different symbols: I ...
- GitHub学习四-查看版本库信息
首先,只有在本地init以及关联到远程版本库的本地版本库,才能查看版本库信息 1.查看版本库名字 如果忘了名字,直接运行git remote 2.git remote show <name> ...
- PyCharm调试运行Scrapy教程
一.使用scrapy创建一个项目 这里使用scrapy官方第一个示例 scrapy startproject tutorial 使用PyCharm打开项目,在tutorial/tutorial/spi ...
- Xmind settings lower
Xmind settings lower 1● setting 2● options 3● fast short keys 快捷键(Windows) 快捷键(Mac) 描述 Ctrl+N ...
- POJ 2373 Dividing the Path(DP + 单调队列)
POJ 2373 Dividing the Path 描述 农夫约翰的牛发现,在他的田里沿着山脊生长的三叶草是特别好的.为了给三叶草浇水,农夫约翰在山脊上安装了喷水器. 为了使安装更容易,每个喷头必须 ...