前言:鸣谢https://www.luogu.com.cn/blog/virus2017/shuweidp。感谢大佬orz

-----------------------------

【引入】

首先要明白数位DP解决的是什么问题。

问题:求出在$[L,R]$内满足条件$f(i)$的$i$的个数。$f(i)$一般不与数的大小有关,而是与数的组成有关。(数的大小对复杂度的影响很小)

【设计搜索】

数位DP一般都用记忆化搜索来实现。

一、记搜过程

从起点向下搜索,到最底层得到方案数,一层一层向上返回答案并累加,最后从搜索起点得到最终答案。

对于$[l,r]$区间问题,我们一般把他转化为两次数位dp,即找$[0,r]$和$[0,l-1]$两段,再将结果相减就得到了我们需要的$[l,r]$。

二、状态设计

问:$dfs$函数需要哪些参量?

1.首先是记录位置的$pos$,记录答案的$st$,最高位限制$limit$。

2.判断前导0的标记$lead$。

3.因为数位DP一般与数的组成有关,所以当前位可能要与前几位进行比较。所以要设置$pre$用来表示前几位。

4.有可能会有其他参量,依据题意而定。

数位DP中能记录的状态最好都记录下来。

【细节分析】

一、前导0标记$lead$

例如,寻找$[0,1000]$内任意相邻两数相等的数。

由题意得:$111,222,888$等都符合题意。但右端点$1000$是四位数,因此我们要从$0000$开始搜,那么$0000$符合题意但$0111,0222,0888$都不符合题意了。

所以我们要加一个前导0标记。

  1.如果当前位是0并且前导0标记$lead$是1,那么$pos+1$继续深搜。

  2.如果前导0标记是1但当前位不是0,那么此位作为最高位继续深搜(注意此时传递参量可能发生变化)。

当然有时候前导0是不需要记录的,因题而异。如果是研究数字组成的话一般就不用标记前导0。

二、最高位标记$limit$

例如,在搜索$[0,555]$时,显然最高位搜索范围是$[0,5]$,而后面的搜索根据最高位搜索发生变化:

  1.当最高位是$[1,4]$时,显然后面范围是$[0,9]$。

  2.当最高位是$5$时,第二位的范围是$[0,5]$。

为了区分两种情况:我们引入$limit$标记:

  1.当前位$limit=1$且取到最高位时,下一位$limit=1$。

  2.当前位$limit=1$但没有取到最高位时,下一位$limit=0$。

  3.当前位$limit=0$,则下一位$limit=0$。

我们设这一位标记是$limit$,能取到的最高位是$res$,那么下一位的标记就是(i==res)$$limit。

三、DP值的记录与使用

DP数组下标记录的是状态,所以如果当前状态和之前搜过的状态完全一样,我们就可以不用继续深搜,直接返回值即可。

举个例子:

假如我们搜索$[0,123456]$中符合条件的数。

现在搜到了$1000??$,我们记录下来了当前位是第五位,且前一位是0的值。

下一次,我们搜到了$1010??$,我们可以不用再深搜,直接返回之前搜过的值即可。

但是!!!!!

假如现在我们搜到了$1234??$我们可不可以返回当前位是第五位,且前一位是4的值?

当然不行。因为之前的值第五位取值范围是$[0,9]$,而现在取值范围是$[0,5]$,答案数显然不一样,不能混为一谈。

联系之前的知识,我们很容易想到:此时$limit=1$。

因此我们得到一个结论:当$limit=1$时,不能记录和取用DP值。

同样,当$lead=1$时,不能记录和取用DP值。

当然,这还是要看具体题意的。在使用DP数组的过程中也可以把所有状态记录下来,就没有那么多麻烦事了……

【模板】

ll dfs(int pos,int pre,int st,……,int lead,int limit)//记搜
{
if(pos>len) return st;//剪枝
if((dp[pos][pre][st]……[……]!=-&&(!limit)&&(!lead))) return dp[pos][pre][st]……[……];//记录当前值
ll ret=;//暂时记录当前方案数
int res=limit?a[len-pos+]:;//res当前位能取到的最大值
for(int i=;i<=res;i++)
{
//有前导0并且当前位也是前导0
if((!i)&&lead) ret+=dfs(……,……,……,i==res&&limit);
//有前导0但当前位不是前导0,当前位就是最高位
else if(i&&lead) ret+=dfs(……,……,……,i==res&&limit);
else if(根据题意而定的判断) ret+=dfs(……,……,……,i==res&&limit);
}
if(!limit&&!lead) dp[pos][pre][st]……[……]=ret;//当前状态方案数记录
return ret;
}
ll part(ll x)//把数按位拆分
{
len=;
while(x) a[++len]=x%,x/=;
memset(dp,-,sizeof dp);//初始化-1(因为有可能某些情况下的方案数是0)
return dfs(……,……,……,……);//进入记搜
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%lld%lld",&l,&r);
if(l) printf("%lld",part(r)-part(l-));//[l,r](l!=0)
else printf("%lld",part(r)-part(l));//从0开始要特判
}
return ;
}

【题目推荐】

【SCOI2009】Windy数

【ZJOI2010】数字计数

【CQOI2016】手机号码

【AHOI2009】同类分布

【SCOI2014】方伯伯的商场之旅

题目难度按照次序。T5比较难,思维题。

-----------------------------------------

后记:我再也不说数位DP是板子题这种话了QAQ

数位DP 学习笔记的更多相关文章

  1. 数位DP学习笔记

    数位DP学习笔记 什么是数位DP? 数位DP比较经典的题目是在数字Li和Ri之间求有多少个满足X性质的数,显然对于所有的题目都可以这样得到一些暴力的分数 我们称之为朴素算法: for(int i=l_ ...

  2. DP学习笔记

    DP学习笔记 可是记下来有什么用呢?我又不会 笨蛋你以后就会了 完全背包问题 先理解初始的DP方程: void solve() { for(int i=0;i<;i++) for(int j=0 ...

  3. MMM 数位dp学习记

    数位dp学习记 by scmmm 开始日期 2019/7/17 前言 状压dp感觉很好理解(本质接近于爆搜但是又有广搜的感觉),综合了dp的高效性(至少比dfs,bfs优),又能解决普通dp难搞定的问 ...

  4. 树形DP 学习笔记

    树形DP学习笔记 ps: 本文内容与蓝书一致 树的重心 概念: 一颗树中的一个节点其最大子树的节点树最小 解法:对与每个节点求他儿子的\(size\) ,上方子树的节点个数为\(n-size_u\) ...

  5. 数位DP复习笔记

    前言 复习笔记第五篇.(由于某些原因(见下),放到了第六篇后面更新)CSP-S RP++. luogu 的难度评级完全不对,所以换了顺序,换了别的题目.有点乱,见谅.要骂就骂洛谷吧,原因在T2处 由于 ...

  6. 斜率优化DP学习笔记

    先摆上学习的文章: orzzz:斜率优化dp学习 Accept:斜率优化DP 感谢dalao们的讲解,还是十分清晰的 斜率优化$DP$的本质是,通过转移的一些性质,避免枚举地得到最优转移 经典题:HD ...

  7. bzoj 1026: [SCOI2009]windy数 & 数位DP算法笔记

    数位DP入门题之一 也是我所做的第一道数位DP题目 (其实很久以前就遇到过 感觉实现太难没写) 数位DP题目貌似多半是问从L到R内有多少个数满足某些限制条件 只要出题人不刻意去卡多一个$log$什么的 ...

  8. 动态 DP 学习笔记

    不得不承认,去年提高组 D2T3 对动态 DP 起到了良好的普及效果. 动态 DP 主要用于解决一类问题.这类问题一般原本都是较为简单的树上 DP 问题,但是被套上了丧心病狂的修改点权的操作.举个例子 ...

  9. [总结] 动态DP学习笔记

    学习了一下动态DP 问题的来源: 给定一棵 \(n\) 个节点的树,点有点权,有 \(m\) 次修改单点点权的操作,回答每次操作之后的最大带权独立集大小. 首先一个显然的 \(O(nm)\) 的做法就 ...

随机推荐

  1. 线下---复习day04

    目录 1 Django与Ajax 2分页器组件 3 forms组件 4cookie与session组件 5中间件组件 6Auth模块 作业: 1 Django与Ajax # 通过ajax向https: ...

  2. HashMap等集合初始化时应制定初始化大小

    阿里巴巴开发规范中,推荐用户在初始化HashMap时,应指定集合初始值大小. 一.原因 这个不用多想,肯定是效率问题,那为什么会造成效率问题呢? 当我们new一个HashMap没有对其容量进行初始化的 ...

  3. ASP.NET CORE MVC用时分析工具MiniProfiler

    ASP.NET CORE MVC用时分析工具MiniProfiler MiniProfiler(https://miniprofiler.com/)是一个轻量级且简单易用的分析工具库,它可以用来分析A ...

  4. scrapy 基础组件专题(五):自定义扩展

    通过scrapy提供的扩展功能, 我们可以编写一些自定义的功能, 插入到scrapy的机制中 一.编写一个简单的扩展 我们现在编写一个扩展, 统计一共获取到的item的条数我们可以新建一个extens ...

  5. Python面向对象01 /面向对象初识、面向对象结构、类、self、实例化对象

    Python面向对象01 /面向对象初识.面向对象结构.类.self.实例化对象 目录 Python面向对象01 /面向对象初识.面向对象结构.类.self.实例化对象 1. 面向对象初识 2. 面向 ...

  6. ShaderLab-坐标转换

    观察空间就是相机的空间 投影矩阵本质就是对x.y.z分量进行不同程度的缩放(z还做了平移),结果就是视锥体近切面远切面变成正方形.视锥体的中心在(0,0). (对于正交相机,这一步已经得到了立方体) ...

  7. 转自fineui论坛:解决fineui框架开发中的Designer.aspx.cs丢失问题

    在开发的时候碰到个问题,本来好好的Edit.aspx  Edit.aspx.cs  Edit.Designer.aspx.cs编辑Edit.aspx然后保存,编译的时候 发现Edit.aspx.cs里 ...

  8. kubernetes+Azure DevOps实现.Net Core项目的自动化部署&均衡负载

    1. 前言 2. Net Core项目本身的准备 2.1 dockerfile 2.2 创建kubernetes用于helm的chart包 2.2.1 说明 2.2.2 chart文件目录和文件组成 ...

  9. HashTable、HashMap与ConCurrentHashMap源码解读

    HashMap 的数据结构 ​ hashMap 初始的数据结构如下图所示,内部维护一个数组,然后数组上维护一个单链表,有个形象的比喻就是想挂钩一样,数组脚标一样的,一个一个的节点往下挂. ​ 我们可以 ...

  10. k8s极简史:K8s多集群技术发展的历史、现状与未来

    引子 随着云原生技术的普及,越来越多的企业使用Kubernetes来管理应用,并且集群规模也呈爆发式增长,企业也亟需应对随集群规模增长而带来的各种挑战.同时,为了更好地提供高可用.弹性伸缩的应用,企业 ...