关于数位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 ----------------------------- [引入] 首先要 ...
随机推荐
- Dubbo 就是靠它崭露头角!(身为开源框架很重要的一点)
Hola,我是 yes. 经过了 RPC 核心和 Dubbo 微内核两篇文章后,今天终于要稍稍深入一波 Dubbo 了. 作为一个通用的 RPC 框架,性能是很重要的一环,而易用性和扩展性也极为重要. ...
- Java 使用线程池执行若干任务
在执行一系列带有IO操作(例如下载文件),且互不相关的异步任务时,采用多线程可以很极大的提高运行效率.线程池包含了一系列的线程,并且可以管理这些线程.例如:创建线程,销毁线程等.本文将介绍如何使用Ja ...
- 【MySQL 高级】架构介绍
MySQL高级 架构介绍 MySQL 简介 MySQL 安装 Docker 安装 参考链接 Linux 安装 参考链接 MySQL 配置文件 log-bin:二进制日志文件.用于主从复制.它记录了用户 ...
- 深入汇编指令理解Java关键字volatile
volatile是什么 volatile关键字是Java提供的一种轻量级同步机制.它能够保证可见性和有序性,但是不能保证原子性 可见性 对于volatile的可见性,先看看这段代码的执行 flag默认 ...
- 如何实现一个简易版的 Spring - 如何实现 Constructor 注入
前言 本文是「如何实现一个简易版的 Spring」系列的第二篇,在 第一篇 介绍了如何实现一个基于 XML 的简单 Setter 注入,这篇来看看要如何去实现一个简单的 Constructor 注入功 ...
- SAP轻松访问会话管理器等设置
对于SAP的登陆后初始界面,是有一个配置表,可以进行设置的,例如隐藏SAP的标准菜单,设置轻松访问页面右边的图片内容等等这一切的设置都可以通过维护SSM_CUST表来实现可以通过SM30来维护内容,该 ...
- JavaScript中eval的替代方法
引自:https://www.cnblogs.com/lxg0/p/7805266.html 通常我们在使用ajax获取到后台返回的json数据时,需要使用 eval 这个方法将json字符串转换成对 ...
- Py编程方法,尾递归优化,map函数,filter函数,reduce函数
函数式编程 1.面向过程 把大的问题分解成流程,按照流程来编写过程 2.面向函数 面向函数编程=编程语言定义的函数+数学意义上的函数先弄出数学意义上的方程式,再用编程方法编写这个数学方程式注意面向函数 ...
- YARN运行流程
- 关掉IE提示“当前安全设置会使计算机有风险”
我们先来看一下IE浏览器出现的提示窗口,该窗口位于最顶端,不点击设置的话,无法进行下一步的操作. 这时我们点击开始按钮 ,在弹出菜单中选择"运行"菜单项. 在打开的Windows运 ...