关于数位DP的学习
---恢复内容开始---
因为最近做比赛经常会出现数位DP,便尝试着去学学看数位DP。
先给出两篇论文的链接:
然后也是寻找了很多大牛的博客,学习了很多(但是没学会囧。),现在先总结一下已经学到的东西
“在信息学竞赛中,有这样一类问题:求给定区间中,满足给定条件的某个D 进制数或
此类数的数量。所求的限定条件往往与数位有关,例如数位之和、指定数码个数、数的大小
顺序分组等等。题目给定的区间往往很大,无法采用朴素的方法求解。此时,我们就需要利
用数位的性质,设计log(n)级别复杂度的算法。解决这类问题最基本的思想就是“逐位确定”
的方法。下面就让我们通过几道例题来具体了解一下这类问题及其思考方法。”——刘聪
事实上,为什么会想到用数位DP来做,就是因为限定条件往往和数位有关,而仔细地朴素的暴力方法中,所做的重复的工作太多。这样的条件会使得DP(记忆化搜索)有用武之地。
目前我所接触到的大多数的题,都是可以通过记录某些值(比如数位等)来减少重复的运算。当然,因为此类题的特殊性,可以编写check函数确定代码的正确性。
再偷用某个大牛的一句话:其实数位DP(或者说所有记忆化搜索)都是可以看做通过搜索来填满状态的值。
首先先想想数位DP的运行模式
如果我们要统计[0,54321]中满足某个条件的个数,需要将其拆分为
[00000,09999][10000,19999],[20000,29999],[30000,39999],[40000,49999],
[50000,50999],[51000,51999],[52000,52999],[53000,53999],
[54000,54099],[54100,54199],[54200,54299],
[54300,54309],[54310,54319],
[54320,54321]
为什么要这么分呢?随便举个例子,如果我们统计过了[0000,9999]中的满足条件(或者其他各种不满足条件的状态)的个数,那么分别在加上前缀,就可以判断出有多少个满足条件的个数。目的是为了将大的区间划分为小的区间进行求解。
因此,总结一句话,数位DP减少的运算量为:前面几位固定,后面几位可以任意取的个数统计。
比如分析一道简单题:HDU 3652,通过我这个渣渣的错误历程来分析一些细节上的问题
先贴错误代码
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 using namespace std;
5 int dp[20][20][20];
6 int num[20];
7 int dfs(int pos,int mod,int pre,int stat,int limit){
8 if(pos==0) return mod==0&&stat;
9 if(!limit&&dp[pos][mod][pre]!=-1) return dp[pos][mod][pre];
10 int ans=0;
11 int end=limit?num[pos]:9;
12 for(int i=0;i<=end;i++){
13 int nmod=(mod*10+i)%13;
14 int nstat=(pre==1&&i==3)||stat;
15 ans+=dfs(pos-1,nmod,i,nstat,limit&&i==end);
16 }
17 if(!limit) dp[pos][mod][pre]=ans;
18 return ans;
19 }
20 int cal(int x){
21 int cnt=0;
22 memset(num,0,sizeof(num));
23 while(x){
24 num[++cnt]=x%10;
25 x/=10;
26 }
27 return dfs(cnt,0,0,0,1);
28 }
29 int main()
30 {
31 int i,j;
32 int n;
33 memset(dp,-1,sizeof(dp));
34 while(scanf("%d",&n)!=EOF){
35 int ans=cal(n);
36 printf("%d\n",ans);
37 }
38 return 0;
39 }
我选取的记录参数有三个:pos当前处理位,mod前缀和余数,还有前一位的数字pre
但是运算结果却始终会小于等于正确的答案,为什么呢?
想了想,发现其实是因为参数含义的问题。
分析一下,如果我将pre作为一个关键参数记录下来,其实我并不能区分我记录的是后面几位能不能随机取的个数。
即当下一次搜索到pos,mod,pre的时候,不能确定前面是否有13,或者以前搜索的DP[pos][mod][pre]中的数的个数是否有13。
因此应该把记录的参数改为pos,mod,stat(表示为记录状态,0为不含13,1为只含前一位为1,2为前缀含有13)。
因此得到下面的AC代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[20][20][3];
int num[20];
int dfs(int pos,int mod,int stat,int limit){
if(pos==0) return mod==0&&stat==2;
if(!limit&&dp[pos][mod][stat]!=-1) return dp[pos][mod][stat];
int ans=0;
int end=limit?num[pos]:9;
for(int i=0;i<=end;i++){
int nmod=(mod*10+i)%13;
int nstat;
if(stat==1&&i==3||stat==2) nstat=2;
else if(i==1) nstat=1;
else nstat=0;
ans+=dfs(pos-1,nmod,nstat,limit&&i==end);
}
if(!limit) dp[pos][mod][stat]=ans;
return ans;
}
int cal(int x){
int cnt=0;
memset(num,0,sizeof(num));
while(x){
num[++cnt]=x%10;
x/=10;
}
return dfs(cnt,0,0,1);
}
int main()
{
int i,j;
int n;
memset(dp,-1,sizeof(dp));
while(scanf("%d",&n)!=EOF){
int ans=cal(n);
printf("%d\n",ans);
}
return 0;
}
好了讲完了
关于数位DP的学习的更多相关文章
- 数位DP::SoSDP
数位DP:: SoSDP 学习博客(待补) 下面做一些例题: SPECIAL PAIRS 题意 给n个数字,求这些数字有多少对的\(AND\) 结果是0.数字不大于1e6.顺序反相反视为不同的对. 思 ...
- HDU3555【数位DP】
入门...还在学习中,先贴一发大牛博客 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555 题目大意: 给一个数字n,范围在1~2^63-1,求1~ ...
- 掌握数位dp
最近遇到了数位dp题目,于是就屁颠屁颠的跑过来学习数位dp了~ "在信息学竞赛中,有这样一类问题:求给定区间中,满足给定条件的某个D 进制数或此类数的数量.所求的限定条件往往与数位有关,例如 ...
- 数位DP学习笔记
数位DP学习笔记 什么是数位DP? 数位DP比较经典的题目是在数字Li和Ri之间求有多少个满足X性质的数,显然对于所有的题目都可以这样得到一些暴力的分数 我们称之为朴素算法: for(int i=l_ ...
- MMM 数位dp学习记
数位dp学习记 by scmmm 开始日期 2019/7/17 前言 状压dp感觉很好理解(本质接近于爆搜但是又有广搜的感觉),综合了dp的高效性(至少比dfs,bfs优),又能解决普通dp难搞定的问 ...
- [学习笔记] 数位DP的dfs写法
跟着洛谷日报走,算法习题全都有! 嗯,没错,这次我也是看了洛谷日报的第84期才学会这种算法的,也感谢Mathison大佬,素不相识,却写了一长篇文章来帮助我学习这个算法. 算法思路: 感觉dfs版的数 ...
- 【学习笔记&训练记录】数位DP
数位DP,即对数位进行拆分,利用数位来转移的一种DP,一般采用记忆化搜索,或者是先预处理再进行转移 一个比较大略的思想就是可以对于给定的大数,进行按数位进行固定来转移记录答案 区间类型的,可以考虑前缀 ...
- ACM学习历程—HDU5587 Array(数学 && 二分 && 记忆化 || 数位DP)(BestCoder Round #64 (div.2) 1003)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5587 题目大意就是初始有一个1,然后每次操作都是先在序列后面添加一个0,然后把原序列添加到0后面,然后 ...
- 数位DP 学习笔记
前言:鸣谢https://www.luogu.com.cn/blog/virus2017/shuweidp.感谢大佬orz ----------------------------- [引入] 首先要 ...
随机推荐
- Kubernetes官方java客户端之八:fluent style
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- 剑指offer 面试题0:扎实的基础:即编程语言、数据结构和算法
编程语言: Q:如果写的函数需要传入一个指针,则是否需要为该指针加上const?把const加在指针不同的位置是否有区别? A:const是用来声明一个常量的,如果不想让一个值改变就应该加上const ...
- Openstack neutron 网络服务 (七)
引用: https://docs.openstack.org/ocata/zh_CN/install-guide-rdo/common/get-started-networking.html neut ...
- Python基础语法6-冒泡排序
用for循环实现冒泡排序(升序): array = [3,2,1] for i in range(len(array) - 1, 0, -1): for j in range(0, i): if ...
- 使用smartform打印表单
昨天写了个smartform打印表单,在开发完成,在测试机测试OK,传到生产机,出现严重问题!无法打印,干脆就是无法调用打印图形界面,进入SMARTFORM事物,查看这个表单,发现,居然公司的LOGO ...
- 【Azure App Service For Container】创建ASP.NET Core Blazor项目并打包为Linux镜像发布到Azure应用服务
欢迎使用 Blazor!Blazor 是一个使用 .NET 生成交互式客户端 Web UI 的框架: 使用 C# 代替 JavaScript 来创建信息丰富的交互式 UI. 共享使用 .NET 编写的 ...
- nodejs内网穿透
说明 本地服务注册,基于子域名->端口映射.公网测试请开启二级或三级域名泛解析 无心跳保活.无多线程并发处理 服务器端 请求ID基于全局变量,不支持PM2多进程开服务端.(多开请修改uid函数, ...
- 前端面试准备笔记之JavaScript(04)
01. DOM的本质 xml是一种可扩展的标记性语言,可扩展就是可以描述任何结构的数据,他是一棵树,可以自定义标签,可以自己扩展. html也是一种特定的xml,他规定了一些标签的名称,结构与xml是 ...
- 记录list.remove()和list.pop()
list.remove(obj):这个是移除列表中某个值的第一个匹配项 list.pop(index):这个是移除列表中下标为index的元素 当元素全是数字或者有数字时注意区分.
- C#高级编程第11版 - 第六章 索引
[1]6.2 运算符 1.&符在C#里是逻辑与运算.管道符号|在C#里则是逻辑或运算.%运算符用来返回除法运算的余数,因此当x=7时,x%5的值将是2. [2]6.2.1 运算符的简写 1.下 ...