预警:由于是从$Vergil$学长那里和$Mathison$大神那里学来的,所以清一色记忆化搜索!qwq

巨佬的数位dp讲解(未来的咕咕日报头条):

https://www.luogu.org/blog/virus2017/shuweidp


数位dp嘛,顾名思义...就是与每个数上的那位有关系...(废话)

一般问题形式都是,在区间$[l,r]$中,满足xx条件的数有多少。然后数据范围一般巨大,枚举是不可能的,这辈子是不可能的。所以一般我们都把它当做字符串处理,预处理出每一位的数(数位),即之后的$border$数组。先求出1~r内满足范围的数,在求出1~l-1范围内的数,运用前缀和的思想一减的。

关于处理成字符串:

一种高精处理方法

        scanf("%s",p+);
int lenp=strlen(p+); int last=lenp;
while(p[last]==''&&last) last--;
for(int i=last+;i<=lenp;i++) p[i]='';
p[last]--; for(int i=;i<=lenp;i++) a[lenp-i+]=p[i]-'';
memset(dp,-,sizeof(dp));
ans1=dfs(lenp,,); scanf("%s",q+);
int lenq=strlen(q+);
for(int i=;i<=lenq;i++) a[lenq-i+]=q[i]-'';
memset(dp,-,sizeof(dp));
ans2=dfs(lenq,,);
printf("%d\n",ans2-ans1);

另一种暴力搞,longlong范围内才可。

        l--;
while(l)
{
border[++len]=l%;
l/=;
}
memset(dp,-,sizeof(dp));
ans1=dfs(len,,);
len=; while(r)
{
border[++len]=r%;
r/=;
}
memset(dp,-,sizeof(dp));
ans2=dfs(len,,);
len=;
printf("%lld\n",ans2-ans1);

记忆化搜索?其实感觉他是dp的本质吧...(说错了不要打我啊qwq)。搜索我们要遍历所有状态,遍历完就溜,啥也不记下来,没心没肺;dp我们也要遍历全部状态,但是长心眼了,懂得存起来,用空间换时间;然后dp是和记忆化搜索学的,所以记忆化搜索也记录了搜过的状态,当现在搜的之前搜过,就直接调用之前的结果。Update:Vergil学长表示他认为dp的本质是最短路 把各状态当作图的节点,然后与开始节点连的边为初始值...


既然我们记搜了,那么肯定我们需要知道每次记录哪些量:一般地,我们会记到$pos$(现在在第几位,个人习惯从右往左为第1位到第$len$位)、$pre$(上一位是哪个数字,用于与上一位有关的情况)、$ling$前导零标记、$limit$最高位标记、$st$是否已经满足要求了,还有其他实际情况需要的。开始从主程序进来的时候,$ling$和$limit$都是1。

图自@Mathison真·大神

另外,当$limit=1$不能记录和调用dp值!当$ling=1$也不能调用记录dp值!


以上是概论,现在分两种题型扯几道例题。

第一种题型:问你数位满足是否有xx数 or 没有xx数

  • 例1 hdu2089不要62

   题目大意:问你$[l,r]$中不含4且不含连续62的数的个数。

   真·入门题。因为不含,所以只要有4或62连续,之后我们就不用搜了。

 #include<cstdio>
#include<algorithm>
#include<cstring> using namespace std;
typedef long long ll; int l,r,len;
int border[];
ll ans1,ans2,dp[][]; ll dfs(int pos,int pre,int limit)
{
if(pos<) return ;
if(!limit && dp[pos][pre]!=-) return dp[pos][pre];
ll tmp=;
ll lim=limit ? border[pos] : ;
for(int i=;i<=lim;i++)
{
if(i==) continue;
if(i==&&pre==) continue;
tmp+=dfs(pos-,i,(limit&&i==lim));
}
if(!limit) dp[pos][pre]=tmp;
return tmp;
} int main()
{
while(scanf("%d%d",&l,&r)!=EOF&&l!=&&r!=)
{
l--;
while(l)
{
border[++len]=l%;
l/=;
}
memset(dp,-,sizeof(dp));
ans1=dfs(len,,);
len=; while(r)
{
border[++len]=r%;
r/=;
}
memset(dp,-,sizeof(dp));
ans2=dfs(len,,);
len=;
printf("%lld\n",ans2-ans1);
}
return ;
}
  • 例2 hdu3555Bomb

   题目大意:问你1~n中含连续49的数的个数。

   数位dp入门题。因为题目问的是“含”,所以我们必须记一个$st$,看之前是否满足。这里就不能像上一道题一样不满足直接$continue$掉了。因为之后还有希望满足啊qwq。

 //求1~n 含有连续49的数字有多少
#include<cstdio>
#include<algorithm>
#include<cstring> using namespace std;
typedef long long ll; int T,len;
int border[];
ll dp[][][];
char sta[]; ll dfs(int pos,int pre,int st,int limit)
{
if(pos<) return st;
if(!limit && dp[pos][pre][st]!=-) return dp[pos][pre][st];
ll tmp=;
ll lim=limit ? border[pos] : ;
for(int i=;i<=lim;i++)
tmp+=dfs(pos-,i,st||(i==&&pre==),limit&&i==lim);
if(!limit) dp[pos][pre][st]=tmp;
return tmp;
} int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%s",sta+);
len=strlen(sta+);
for(int i=;i<=len;i++)
border[i]=sta[len-i+]-'';
memset(dp,-,sizeof(dp));
printf("%lld\n",dfs(len,,,));
}
return ;
}
  • 例3 poj3208 Apocalypse Someday

第2种题型:问你数位是否满足一些比较复杂的要求(就不是上面那么简单了)

例题会尽量都补上的qwq

关于输出方案:本来想试着输出下方案的,后来发现实际输出的时候其实会少很多。因为我们记忆化!当一个状态下的值已经确定了,我们就不再切身实际地搜,而是直接调用之前结果了,也就方案会少输出了。当然,没记忆化的时候,显然我们可以输出全部方案。

数位dp。。。按Mathison大佬的话,还是非常套路模板化的,唯一要注意的是举一反三和前导零的处理...。

数位dp真·浅谈 By cellur925的更多相关文章

  1. 【hdu3652】数位dp(浅尝ACM-A)

    向大佬学习 第一次写博客有点紧张,也算是小萌新的突破吧 这次主要是总结一下校内的ACM比赛的各种题,主要是新思路以及学到的新知识 先放一张 下面开始说正事 题面 A wqb-number, or B- ...

  2. 真·浅谈treap树

    treap树是一种平衡树,它有平衡树的性质,满足堆的性质,是二叉搜索树,但是我们需要维护他 为什么满足堆的性质?因为每个节点还有一个随机权值,按照随机权值维持这个堆(树),可以用O(logn)的复杂度 ...

  3. 线性DP LIS浅谈

    LIS问题 什么是LIS? 百度百科 最长上升子序列(Longest Increasing Subsequence,LIS),在计算机科学上是指一个序列中最长的单调递增的子序列. 怎么求LIS? O( ...

  4. Ural1057. Amount of Degrees 题解 数位DP

    题目链接: (请自行百度进Ural然后查看题号为1057的那道题目囧~) 题目大意: Create a code to determine the amount of integers, lying ...

  5. 浅谈数位DP

    在了解数位dp之前,先来看一个问题: 例1.求a~b中不包含49的数的个数. 0 < a.b < 2*10^9 注意到n的数据范围非常大,暴力求解是不可能的,考虑dp,如果直接记录下数字, ...

  6. 数位dp浅谈(hdu3555)

    数位dp简介: 数位dp常用于求区间内某些特殊(常关于数字各个数位上的值)数字(比如要求数字含62,49): 常用解法: 数位dp常用记忆化搜索或递推来实现: 由于记忆化搜索比较好写再加上博主比较蒟, ...

  7. 浅谈状态压缩DP

    浅谈状态压缩DP 本篇随笔简单讲解一下信息学奥林匹克竞赛中的状态压缩动态规划相关知识点.在算法竞赛中,状压\(DP\)是非常常见的动规类型.不仅如此,不仅是状压\(DP\),状压还是很多其他题目的处理 ...

  8. LuoguP2602 [ZJOI2010]数字计数【数位dp】By cellur925

    题目传送门 题目大意:给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次. 继续数位dp=w=. 这一次我们不需要记录$pre$啦!(撒花). 因为这次我们需要的 ...

  9. LuoguP2657 [SCOI2009]windy数 【数位dp】By cellur925

    题目传送门 题目大意:在A和B之间,包括A和B,总共有多少个不含前导零且相邻两个数字之差至少为2的正整数? 显然是数位dp啦=w=. 显然与上一位有关,我们$dfs$的时候就要记录$pre$.因为这是 ...

随机推荐

  1. C指针——C语言手记

    近期敲代码的时候.发现自己非常多东西都開始忘了. 今天最终有机会好好总结一下指针.当做个笔记同一时候也希望对大家实用.假设有不对的地方.希望大家能帮我指正一下.然后我的实验环境是32位RHEL+ecl ...

  2. Qt浅谈之二十一log调试日志

    一.简单介绍 近期因调试code时,想了解程序的流程,但苦于没有一个简易的日志记录,不停使用qDebug打印输出,而终于提交代码时得去多次删除信息打印,有时还会出现新改动的代码分不清是哪些部分.而使用 ...

  3. VC++ 学习笔记(四):停止还是暂停这个系列

    我已经很久没有更新这个话题了,原因是多方面的,比如比较忙,比如我参与的项目不使用C++.最近因为需要在C#的客户端中调用第三方的C++API,又想起了这个话题.在跟公司里的C++方面专家聊过之后,我有 ...

  4. BootLoader与Linux内核的参数传递【转】

    本文转载自:http://blog.sina.com.cn/s/blog_476d8cf30100rttx.html 在嵌入式系统中,BootLoader 是用来初始化硬件,加载内核,传递参数.因为嵌 ...

  5. POJ3984 迷宫问题 —— BFS

    题目链接:http://poj.org/problem?id=3984 迷宫问题 Time Limit: 1000MS   Memory Limit: 65536K Total Submissions ...

  6. FZU2150 Fire Game —— BFS

    题目链接:https://vjudge.net/problem/FZU-2150 Problem 2150 Fire Game Accept: 2702    Submit: 9240 Time Li ...

  7. YTU 2391: 求素数

    2391: 求素数 时间限制: 1 Sec  内存限制: 128 MB 提交: 116  解决: 3 题目描述 设计一个程序,输出所有小于等于n(n为一个大于2的正整数)的素数. 要求:(1)每行输出 ...

  8. 第五届蓝桥杯C++B组省赛

    1.啤酒和饮料 2.切面条 3.李白打酒 4.史丰收速算 5.打印图形 6.奇怪的分式 7.六角填数 8.蚂蚁感冒 9.地宫取宝 10.小朋友排队

  9. Opencv中视频播放与进度控制

    视频画面本质上是由一帧一帧的连续图像组成的,播放视频其实就是在播放窗口把一系列连续图像按一定的时间间隔一幅幅贴上去实现的. 人眼在连续图像的刷新最少达到每秒24帧的时候,就分辨不出来图像间的闪动了,使 ...

  10. I.MX6 AW-NB177NF p2p support

    /***************************************************************************** * I.MX6 AW-NB177NF p2 ...