算法使用范围

在一个区间里面求有多少个满足题目所给的约束条件的数,约束条件必须与数自身的属性有关

下面用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详解的更多相关文章

  1. 数位DP 详解

    序 天堂在左,战士向右 引言 数位DP在竞赛中的出现几率极低,但是如果不会数位DP,一旦考到就只能暴力骗分. 以下是数位DP详解,涉及到的例题有: [HDU2089]不要62 [HDU3652]B-n ...

  2. 动态规划晋级——HDU 3555 Bomb【数位DP详解】

    转载请注明出处:http://blog.csdn.net/a1dark 分析:初学数位DP完全搞不懂.很多时候都是自己花大量时间去找规律.记得上次网络赛有道数位DP.硬是找规律给A了.那时候完全不知数 ...

  3. 数位dp详解&&LG P2602 [ZJOI2010]数字计数

    数位dp,适用于解决一类求x~y之间有多少个符合要求的数或者其他. 例题 题目描述 杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除 ...

  4. 状压DP详解(位运算)

    前言: 状压DP是一种非常暴力的做法(有一些可以排除某些状态的除外),例如dp[S][v]中,S可以代表已经访问过的顶点的集合,v可以代表当前所在的顶点为v.S代表的就是一种状态(二进制表示),比如 ...

  5. 状态压缩dp 状压dp 详解

    说到状压dp,一般和二进制少不了关系(还常和博弈论结合起来考,这个坑我挖了还没填qwq),二进制是个好东西啊,所以二进制的各种运算是前置知识,不了解的话走下面链接进百度百科 https://baike ...

  6. 线性DP详解

    顾名思义,线性DP就是在一条线上进行DP,这里举一些典型的例子. LIS问题(最长上升子序列问题) 题目 给定一个长度为N的序列A,求最长的数值单调递增的子序列的长度. 上升子序列B可表示为B={Ak ...

  7. 状态压缩动态规划(状压DP)详解

    0 引子 不要999,也不要888,只要288,只要288,状压DP带回家.你买不了上当,买不了欺骗.它可以当搜索,也可以卡常数,还可以装B,方式多样,随心搭配,自由多变,一定符合你的口味! 在计算机 ...

  8. 状压DP详解+题目

    介绍 状压dp其实就是将状态压缩成2进制来保存 其特征就是看起来有点像搜索,每个格子的状态只有1或0 ,是另一类非常典型的动态规划 举个例子:有一个大小为n*n的农田,我们可以在任意处种田,现在来描述 ...

  9. 树形DP详解+题目

    关于树形dp 我觉得他和线性dp差不多 总结 最近写了好多树形dp+树形结构的题目,这些题目变化多样能与多种算法结合,但还是有好多规律可以找的. 先说总的规律吧! 一般来说树形dp在设状态转移方程时都 ...

随机推荐

  1. Spring Boot之logback日志最佳实践

    一.Spring Boot日志介绍 Spring Boot对所有内部日志记录使用了Commons Logging,但是底层日志实现是开放的.为Java Util日志记录.Log4J2和Logback提 ...

  2. centos 安装 和 linux 简单命令

    1. centos 安装 参照:https://www.cnblogs.com/tiger666/articles/10259102.html 安装过程注意点: 1. 安装过程中的选择安装Basic ...

  3. 小程序for循环嵌套

    <view class='nocontnt' wx:if="{{listLength == 0 }}"> 暂无相关评论 </view> <view c ...

  4. PHP单例模式实例,连接数据库对类的引用

    <?php//单例模式连接数据库class pzhang{ static private $instance; private static $config; private $dbase = ...

  5. 【其他】【PL/SQL Developer】【1】解决PL/SQL Developer过期的情况

    正文: 1,开始菜单,搜索regedit,回车打开(即日常搜索电脑安装的软件的地方,regedit就是注册表) 2,按HKEY_CURRENT_USER\Software\Allround Autom ...

  6. 整合多个网络的拓扑结构并降维(Mashup)

    整合多个网络的拓扑结构并降维(Mashup) 介绍一个整合多个网络拓扑结构的方法,方法来源:Compact Integration of Multi-Network Topology for Func ...

  7. ThinkPHP3的使用

    1. 初始目录 7d 根目录 ├─Application 应用目录(空) ├─Public 资源文件目录 ├─ThinkPHP 框架目录 └─index.php 入口文件 2. 入口文件 // 应用入 ...

  8. PostgreSQL主备流复制机制

    原文出处 http://mysql.taobao.org/monthly/2015/10/04/ PostgreSQL在9.0之后引入了主备流复制机制,通过流复制,备库不断的从主库同步相应的数据,并在 ...

  9. centos命令行系列之升级glibc到

    1.从http://ftp.gnu.org/gnu/glibc/glibc-2.17.tar.gz 下载文件 2.安装部署 [root@kafzook1 /]# tar -xf glibc-2.17. ...

  10. Hadoop格式化 From hu-hadoop1/192.168.11.11 to hu-hadoop2:8485 failed on connection exception: java.net.

    192.168.11.12:8485: Call From hu-hadoop1/192.168.11.11 to hu-hadoop2:8485 failed on connection excep ...