数位DP学习笔记

  • 什么是数位DP?

  数位DP比较经典的题目是在数字Li和Ri之间求有多少个满足X性质的数,显然对于所有的题目都可以这样得到一些暴力的分数

  我们称之为朴素算法:

for(int i=l_i;i<=r_i;i++)
if(check(i)) ans++;
return ans;

所有的算法都是为了减少运算步骤这一个基本原理来优化的,我们考虑这样暴力的优化,显然数的位数上面满足X性质,有些时候X性质并不是单单对于一个数的个体进行限制的

而是在某个限定区域里面的所有数字有一个X的限制,这意味着我们只要选取一定的方法,将这一部分数字除掉,其他位数的数字按照一定方法(规律填下去)就能够避免冗余运算

而且题目让我们求这样的数字有多少个而不是求出所有这样的数字,这样对于在数位上的Dp有一定的余地可以使用。

所以为了解决数位上面一些显然的规律递推,数位DP就应运而生了。

还有奶一口李建说数位DP NOIP2018会考!!!(不然我学什么学)

  • 怎么做数位DP的题目?(模板)

记忆化搜索大法好!

首先有这样的前缀和思想如果我们知道了[1,Ti]中满足X性质的数的个数

现在要求[Li,Ri]之间满足X性质的数的个数那么答案就是[1,Ri]中满足X性质的数个数减去[1,Li-1]满足X性质的数个数

对于一般的数位Dp的题目当前位置的取值能取和不可取取决于前面的某个数或者某个性质X',我们不妨在dfs搜索的时候

把这个性质X‘记录下来然后确定出当前位置的取值范围,然后再进行搜索

当然在这之中可能会存在冗余搜索,我们只要记忆化就行了,这样子,最后的复杂度就比暴力枚举要可观的多了!!!

# include <bits/stdc++.h>
using namespace std;
int dfs(int pos/*位置*/,/*状态*/,bool lead/*有无前导零*/,bool limt/*数位到上界标识*/)
{
if(pos==-) return ;
if (!limt&&!lead&&dp[pos][state]!=-) return dp[pos][state];
int up=limt?a[pos]:;
int ans=;
for (int i=;i<=up;i++) {
if () ...
else if () ...
ans+=dfs(pos-,/*状态转移*/,lead&&i==,limt&&i==a[pos]);
}
if (!limt&&!lead) dp[pos][state]=ans;
return ans;
}
void solve(int x)
{
int pos=;
while (x) a[pos++]=x%,x/=;
return dfs(pos-,/*初始状态*/,,);
}
int main()
{
int l,r;
while (~scanf("%d%d",&l,&r)) {
printf("%d\n",solve(r)-solve(l-));
}
return ;
}

解释一下吧,显然数位DP也是一个DP也是存在DP的一系列限制,如无后效性等特性。

我们现在通过数位DP这个框架枚举状态而不是枚举数字了,也就是说你能够通过你记录的所有状态推出现在所有的可行解的数目

所以你设置状态的时候要包含所有可能性,不重不漏,满足所有他的子状态加起来得到的和就是母状态的值

解释一下程序吧,后面两个就不看了直接看dfs的程序:

int dfs(int pos/*位置*/,/*状态*/,bool lead/*有无前导零*/,bool limt/*数位到上界标识*/)
{
if(pos==-) return ;
if (!limt&&!lead&&dp[pos][state]!=-) return dp[pos][state];
int up=limt?a[pos]:;
int ans=;
for (int i=;i<=up;i++) {
if () ...
else if () ...
ans+=dfs(pos-,/*状态转移*/,lead&&i==,limt&&i==a[pos]);
}
if (!limt&&!lead) dp[pos][state]=ans;
return ans;
}

pos的话就是当前枚举那个位子每次-1,假设数位从0开始标号那么pos=-1的时候就结束了返回1,之后是记录一些状态,满足这些状态下满足条件的值(比pos位还要低的位数这样后面推更高位的时候可以重复利用)

然后lead表示这个状态是不是包含前导零(对于一些题目有无前导零不影响计数为方便起见就不设状态,然后是limt是数位上界标识就是前面若干位已经是最大了,不用更大的范围,当前枚举最大值只需要恰好0到a[pos]就够用了)

然后后面一句话就是记忆化搜索(在相同的位置有相同的状态就有相同的答案),然后是求出这位的取值范围,然后根据题目条件判断答案可行,把可行的子状态加入到母状态的解中,最后满足没有限制时候记忆化搜索的结果。

解释一下lead和limt标识:

    • lead指代的是:此时的数含不含前导0(显然一开始是在前面是0的情况下做的,含前导0)
    • lim指代的是:此时这位的上面几位(到头)是不是都是最大值了这样此时当前位置最大值不能为9否则就超了!
  • 几个好的博客
  • 实战演练

sol:

# include <bits/stdc++.h>
# define int long long
using namespace std;
int a[];
int dp[][];
int dfs(int pos,int pre,bool state,bool lim)
// 到pos位,前一个数为pre,当前有没有限制,是否是lim
{
if (pos==-) return ;
if (!lim&&dp[pos][state]!=-) return dp[pos][state];
int up=lim?a[pos]:;
int ans=;
for (int i=;i<=up;i++) {
if (i==) continue;
if (pre==&&i==) continue;
ans+=dfs(pos-,i,i==,lim&&i==a[pos]);
}
if (!lim) dp[pos][state]=ans;
return ans;
}
int solve(int x)
{
int o=;
while (x) {
a[o++]=x%;
x/=;
}
return dfs(o-,,,);
}
signed main()
{
memset(dp,-,sizeof(dp));
while () {
int l,r;
scanf("%lld%lld",&l,&r);
if (l==&&r==) break;
printf("%lld\n",solve(r)-solve(l-));
}
return ;
}

洛谷P2657 SCOI2009 Windy数

sol:这道题和不要62这道题不同的是要处理前导是0的情况这里我们采取特判的形式,如果此时的p是在前导有0的情况下那么就把pre设置为负无穷

这样不会对后面有影响。注意这里再次强调一下lead和lim,

lead指代的是:此时的数含不含前导0(显然一开始是在前面是0的情况下做的,含前导0)

lim指代的是:此时这位的上面几位(到头)是不是都是最大值了这样此时当前位置最大值不能为9否则就超了!

# include <bits/stdc++.h>
# define int long long
using namespace std;
int dp[][],a[];
int dfs(int pos,int pre,bool lead,bool lim)
{
if (pos==-) return ;
if (!lim&&!lead&&dp[pos][pre]!=-) return dp[pos][pre];
int up=lim?a[pos]:;
int ans=;
for (int i=;i<=up;i++) {
if (abs(i-pre)>=) {
int p=lead&&i==?-0x7f7f7f7f:i;
ans+=dfs(pos-,p,lead && i==,lim && i==a[pos]);
}
}
if (!lim&&!lead) dp[pos][pre]=ans;
return ans;
}
int solve(int x)
{
int pos=;
while (x) {
a[pos++]=x%;
x/=;
}
return dfs(pos-,-0x7f7f7f7f,,);
}
signed main()
{
memset(dp,-,sizeof(dp));
int l,r;
scanf("%lld%lld",&l,&r);
printf("%lld\n",solve(r)-solve(l-));
return ;
}
  •  数位DP进阶题目选讲
  P2518 [HAOI2010]计数
  P3281 [SCOI2013]数数
  P3303 [SDOI2013]淘金

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

  1. 数位DP 学习笔记

    前言:鸣谢https://www.luogu.com.cn/blog/virus2017/shuweidp.感谢大佬orz ----------------------------- [引入] 首先要 ...

  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. CGAL学习:数据类型

    CGAL 4.13 - Number Types 1 Introduction(介绍:略) 涉及到的数大致有3种:一是整数,二是有理数,三是浮点数.有理数可以用2个整数表示.精度上可分为任意精度和固定 ...

  2. kettle学习笔记(九)——子转换、集群与变量

    一.概述 kettle中3个重要的步骤: 子转换/映射 在转换里调用一个子转换,便于封装和重用. 集群 集群模式 变量和参数 变量和参数的用法 二.子转换 1.定义子转换 主要由映射输入与映射输出定义 ...

  3. 20155204《网络对抗》Exp 6 信息搜集与漏洞扫描

    20155204<网络对抗>Exp 6 信息搜集与漏洞扫描 一.实验后回答问题 1.哪些组织负责DNS,IP的管理. 互联网名称与数字地址分配机构,简称ICANN机构,决定了域名和IP地址 ...

  4. VS与Opencv的亲密接触之安装配置过程

    最近想把FPGA采集的图像,上传到上位机显示,看到Opencv能帮大忙,所以就折腾折腾! 我用的是VS2012和opencv-2.4.10-2.4.10(目前的最新版本),那个版本无所谓,本文都将适用 ...

  5. coco2d-x游戏逻辑结构

    在Cocos2d-x中开发游戏的主要逻辑和结构是:先创建场景,在场景上添加一层或多层,然后可以在指定层上添加精灵.菜单.文字等,可以为精灵.文字执行某个动作(或者移动),检测玩家触屏事件,开启任务调度 ...

  6. vue JointJS 实例demo

    前言 越来越发现,前端深入好难哦!虐成渣渣了. 需求:前端绘制灵活的关系图(此demo还是简单的,我的需求才跨出一小步) 安装 npm install jointjs 容器,工具栏 <templ ...

  7. Apache Ignite 学习笔记(四): Ignite缓存冗余备份策略

    Ignite的数据网格是围绕着基于内存的分布式key/value存储能力打造的.当初技术选型的时候,决定用Ignite也是因为虽然同样是key/value存储,它有着和其他key/value存储系统不 ...

  8. Kubernetes并发控制与数据一致性的实现原理

    在大型分布式系统中,定会存在大量并发写入的场景.在这种场景下如何进行更好的并发控制,即在多个任务同时存取数据时保证数据的一致性,成为分布式系统必须解决的问题.悲观并发控制和乐观并发控制是并发控制中采用 ...

  9. PAT甲题题解-1112. Stucked Keyboard (20)-(map应用)

    题意:给定一个k,键盘里有些键盘卡住了,按一次会打出k次,要求找出可能的坏键,按发现的顺序输出,并且输出正确的字符串顺序. map<char,int>用来标记一个键是否为坏键,一开始的时候 ...

  10. Daily Scrum NO.4

    工作概况 符美潇(PM) 昨日完成的工作 1.Daily Scrum.日常会议及日常工作的分配和查收. 2.解决并录入了一个严重的过滤器BUG,该BUG会导致获取子链接的严重异常. 3.在TFS上进行 ...