数位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在设状态转移方程时都 ...
随机推荐
- 【IOS学习】【Swift语言】
基本语法: OS X playground 引入 import Cocoa IOS playground 引入 import UIKit 基本数据类型 let 定义常量 定义完成之后无法修改 var ...
- ajax被cancel问题(事件冒泡)
发送ajax请求的时候发现ajax请求总是被cancel,但是请求却被执行了,查阅了知识之后,发现问题是:事件冒泡,记录下来,供自己和大家学习借鉴. 1. 前提,发出ajax的请求在form表单中 2 ...
- mybatis中的mapper接口文件以及selectByExample类的实例函数详解
记录分为两个部分,第一部分主要关注selectByExample类的实例函数的实现:第二部分讨论Mybatis框架下基本的实例函数. (一)selectByExample类的实例函数的实现 当你启动项 ...
- hdu5608杜教筛
题意:给定函数\(f(x)\),有\(n^2-3*n+2=\sum_{d|n}f(d)\),求\(\sum_{i=1}^nf(i)\) 题解:很显然的杜教筛,假设\(g(n)=n^2-3*n+2\), ...
- ACM-ICPC 2018 南京赛区网络预赛Skr
题意:求本质不同的回文子串的和 题解:先构造pam,然后根据pam的原理(ch表示在该节点表示的回文串两侧加上该字符)对于每个节点维护一个表示该节点字符串的值,加起来即可 //#pragma GCC ...
- 第二阶段——个人工作总结DAY05
1.昨天做了什么:将值由一个活动传递到另一个活动. 2.今天打算做什么:打算制作修改密码的界面. 3.遇到的困难:因为是任务是分开的,所需要获取的值是通过另一个活动(不是自己任务)的传递过来的,所以还 ...
- Leetcode 969. 煎饼排序
969. 煎饼排序 显示英文描述 我的提交返回竞赛 用户通过次数134 用户尝试次数158 通过次数135 提交次数256 题目难度Medium 给定数组 A,我们可以对其进行煎饼翻转:我们选择 ...
- oracle 分组查询
常用的函数: ·:统计个数:COUNT(),根据表中的实际数据量返回结果: ·:求和:SUM(),是针对于数字的统计,求和 ·:平均值 ...
- Xcode下的中文乱码问题
Xcode下的中文乱码问题 转载自:http://linyehui.me/2014/07/09/convert-gbk-to-utf8-on-mac.html =========== 问题原因 绝大部 ...
- MongoDB,无模式文档型数据库简介
MongoDB的名字源自一个形容词humongous(巨大无比的),在向上扩展和快速处理大数据量方面,它会损失一些精度,在旧金山举行的MondoDB大会上,Merriman说:“你不适宜用它来处理复杂 ...