关于数位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 ----------------------------- [引入] 首先要 ...
随机推荐
- .NET 5网络操作的改进
随着.net 5在11月的发布,现在是谈论网络栈中许多改进的好时机.这包括对HTTP.套接字.与网络相关的安全性和其他网络通信的改进.在这篇文章中,我将重点介绍一些版本中更有影响力和更有趣的变化. H ...
- 【MySQL】DDL数据定义语言的基本用法create、drop和alter(增删改)
DDL 的基础语法 文章目录 DDL 的基础语法 对数据库进行定义 对数据表进行定义 创建表结构(数据表) 设计工具 修改表结构 小结 参考资料 简单复习一波 SQL必知必会 DDL 的英文全称是 D ...
- 环境变量IFS
环境变量IFS的值是由1个空格.1个制表符.1个换行符依序构成的字符串,也就是" \t\n"字符串. #查看IFS变量值的长度: test ~ # expr length &quo ...
- playwright自动化项目搭建
这是关于playwright系列介绍的最后一篇.搭建基于 playwright 的自动化项目. GitHub地址: https://github.com/defnngj/playwright-pro ...
- SQL -去重Group by 和Distinct的效率
经实际测试,同等条件下,5千万条数据,Distinct比Group by效率高,但是,这是有条件的,这五千万条数据中不重复的仅仅有三十多万条,这意味着,五千万条中基本都是重复数据. 为了验证,重复数据 ...
- SELECT ... FOR UPDATE or SELECT ... FOR SHARE Locking Reads session
小结: 1.注意使用限制 Locking reads are only possible when autocommit is disabled (either by beginning transa ...
- WireShark 之 text2pcap
前言 本来想用 010Editer 的,看到破解教程头都大了,那么就用 WireShark 的 Text2pcap 吧! 正文 打开CMD控制台窗口,转到WireShark安装目录 ,此处可以shif ...
- C++ Primer Plus读书笔记(七)函数
1.声明 void fun1(); //该声明在C中的意思是不指定参数,但是在C++中是指定参数为void void func2(...); //C++中不指定参数 2.函数参数为数组区间 STL有数 ...
- 四. Ribbon负载均衡服务调用
1. 概述 1.1 Ribbon是什么 SpringCloud Ribbon是基于Netflix Ribbon实现的一套客户端,是负载均衡的工具. Ribbon是Netflix发布的开源项目,主要功能 ...
- WPF mvvm 验证,耗时两天的解决方案
常用类 类名 介绍 ValidationRule 所有自定义验证规则的基类.提供了让用户定义验证规则的入口. ExceptionValidation 表示一个规则,该规则检查在绑定源属性更新过程中引发 ...