数位 dp 总结
数位 dp 总结
特征
问你一个区间 \([L,R]\) 中符合要求的数的个数
一个简单的 trick :把答案拆成前缀和 \(Ans(R)-Ans(L-1)\)
如何求 \(Ans()\) ,就要用到数位 dp
核心
其实就是记忆化搜索,不建议用循环实现,一是麻烦,二是不会
一般地,设 \(f_{x,\cdots,op}\) 为从最高位到第 \(x\) 位满足一些状态时是否时上一位是否是最高位
只要先将 \(v\) 按照题目要求拆到数组里,然后进行 dfs 算答案即可
递归边界:若 \(x=0\) ,检查当前状态是否合法并返回
有前导零的情况,需要多一个参数
int dfs(int x,...,int op) {
if(!x)return ...;
if(~f[x][...][op])
return f[x][...][op];
register int mx=op?num[x]:1,res=0;
for(int i=0;i<=mx;i++)
res+=dfs(x-1,...,op&(i==mx));
return f[x][...][op]=res;
}
例 1 windy 数
求区间内有多少 不含前导零且相邻两个数字之差至少为 2 的正整数
记录上一个数是多少即可,还可以顺便判断前导零的情况
#include<bits/stdc++.h>
using namespace std;
int L,R,len,num[20],f[20][20][2];
int dfs(int x,int ls,int op) {
if(!x)return 1;
if(~f[x][ls][op])return f[x][ls][op];
register int mx=op?num[x]:9,res=0;
for(int i=0;i<=mx;i++) {
if(abs(ls-i)<2)continue;
if(!i && ls==15)
res+=dfs(x-1,15,op&(i==mx));
else res+=dfs(x-1,i,op&(i==mx));
}
return f[x][ls][op]=res;
}
inline int Ans(int x) {
memset(f,-1,sizeof(f));
len=0;
for(;x;x/=10)num[++len]=x%10;
return dfs(len,15,1);
}
int main() {
while(scanf("%d%d",&L,&R)!=EOF)
printf("%d",Ans(R)-Ans(L-1));
}
例 2 Round Numbers S
如果一个正整数的二进制表示中,0 的数目不小于 1 的数目,那么它就被称为「圆数」。
计算区间 \([l,r]\) 中有多少个「圆数」。
分别记录 0 的个数和 1 的个数,需要判断前导零,记得用二进制
#include<bits/stdc++.h>
using namespace std;
int L,R,len,num[40],f[40][40][40][2];
int dfs(int x,int s0,int s1,int op,int fir) {
if(!x)return s0>=s1;
if(~f[x][s0][s1][op])
return f[x][s0][s1][op];
register int mx=op?num[x]:1,res=0;
for(int i=0;i<=mx;i++) {
if(fir && !i)res+=dfs(x-1,0,0,op&(i==mx),1);
else res+=dfs(x-1,s0+(i==0),s1+(i==1),op&(i==mx),0);
}
return f[x][s0][s1][op]=res;
}
inline int Ans(int x) {
memset(f,-1,sizeof(f)),len=0;
while(x)num[++len]=x&1,x>>=1;
return dfs(len,0,0,1,1);
}
int main() {
scanf("%d%d",&L,&R);
printf("%d",Ans(R)-Ans(L-1));
}
例 3 [CQOI2016]手机号码
计算区间 \([l,r]\) 中有多少个满足
- 有三个相同数相邻
- 不能同时出现 8 和 4
记录上一个数 \(l1\),上上个数 \(l2\),是否有连续三个 \(p3\),是否有 8 \(p8\),是否有 4 \(p4\)
如果只用考虑一个前导零可以直接枚举第一位
坑:如果不满足位数要返回 0
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL L,R,f[20][10][10][2][2][2][2];
int num[20],len;
LL dfs(int x,int l1,int l2,int p3,int p4,int p8,int op) {
if(p4 && p8)return 0;
if(!x)return p3;
if(~f[x][l1][l2][p3][p4][p8][op])
return f[x][l1][l2][p3][p4][p8][op];
register int mx=op?num[x]:9;
register LL res=0;
for(int i=0;i<=mx;i++)
res+=dfs(x-1,i,l1,p3|(i==l1 && i==l2),p4|(i==4),p8|(i==8),op&(i==mx));
f[x][l1][l2][p3][p4][p8][op]=res;
return res;
}
inline LL Ans(LL x) {
memset(f,-1,sizeof(f)),len=0;
while(x)num[++len]=x%10,x/=10;
if(len^11)return 0;
register LL ans=0;
for(int i=1;i<=num[len];i++)
ans+=dfs(10,i,0,0,i==4,i==8,i==num[len]);
return ans;
}
int main() {
scanf("%lld%lld",&L,&R);
printf("%lld",Ans(R)-Ans(L-1));
}
最后
dp 类还是要多练,这几个只是冰山一角
数位 dp 总结的更多相关文章
- 【BZOJ1662】[Usaco2006 Nov]Round Numbers 圆环数 数位DP
[BZOJ1662][Usaco2006 Nov]Round Numbers 圆环数 Description 正如你所知,奶牛们没有手指以至于不能玩"石头剪刀布"来任意地决定例如谁 ...
- bzoj1026数位dp
基础的数位dp 但是ce了一发,(abs难道不是cmath里的吗?改成bits/stdc++.h就过了) #include <bits/stdc++.h> using namespace ...
- uva12063数位dp
辣鸡军训毁我青春!!! 因为在军训,导致很长时间都只能看书yy题目,而不能溜到机房鏼题 于是在猫大的帮助下我发现这道习题是数位dp 然后想起之前讲dp的时候一直在补作业所以没怎么写,然后就试了试 果然 ...
- HDU2089 不要62[数位DP]
不要62 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- 数位DP GYM 100827 E Hill Number
题目链接 题意:判断小于n的数字中,数位从高到低成上升再下降的趋势的数字的个数 分析:简单的数位DP,保存前一位的数字,注意临界点的处理,都是套路. #include <bits/stdc++. ...
- 数位dp总结
由简单到稍微难点. 从网上搜了10到数位dp的题目,有几道还是很难想到的,前几道基本都是模板题,供入门用. 点开即可看题解. hdu3555 Bomb hdu3652 B-number hdu2089 ...
- 数位DP入门
HDU 2089 不要62 DESC: 问l, r范围内的没有4和相邻62的数有多少个. #include <stdio.h> #include <string.h> #inc ...
- 数位DP之奥义
恩是的没错数位DP的奥义就是一个简练的dfs模板 int dfs(int position, int condition, bool boundary) { ) return (condition ? ...
- 浅谈数位DP
在了解数位dp之前,先来看一个问题: 例1.求a~b中不包含49的数的个数. 0 < a.b < 2*10^9 注意到n的数据范围非常大,暴力求解是不可能的,考虑dp,如果直接记录下数字, ...
- 数位DP
题意:(hdu 4734) 我们定义十进制数x的权值为f(x) = a(n)*2^(n-1)+a(n-1)*2(n-2)+...a(2)*2+a(1)*1,a(i)表示十进制数x中第i位的数字. 题目 ...
随机推荐
- JavaScript实现有农历和节气节假日的日历
运行效果: 源代码: 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content ...
- Thread中,run方法和start方法的区别
1. 通过调用Thread类中的start()方法可以启动一个线程,但是线程并不是立刻运行,而是处于就绪态,一旦获取cpu时间片,则会立即运行run()方法 2. start()方法实现了多线程运行, ...
- Python-术语对照表
>>> 交互式终端中默认的 Python 提示符.往往会显示于能以交互方式在解释器里执行的样例代码之前. ... 具有以下含义: 交互式终端中输入特殊代码行时默认的 Python 提 ...
- Java语言学习day08--7月7日
###13遍历数组 * A:遍历数组 * 在操作数组时,经常需要依次访问数组中的每个元素,这种操作称作数组的遍历 * B:练习 public class ArrayDemo04 { publ ...
- jointJS初使用随记
jointJs使用随记 1.下载与安装 前提:一个健康良好且干净的vue脚手架项目. 还是普遍的安装方式 yarn:yarn add jointjs npm:npm install jointjs 还 ...
- 清除安装的Rancher、K8s
#清除主机的所有容器.挂载.镜像(慎用) docker stop $(docker ps -aq) docker system prune -f docker volume rm $(docker v ...
- 【HCIE】ipv6之6to4隧道如何计算48位前缀地址
6to4隧道支持router-router,host-router,router-host,host-host 采用专用6to4地址,前缀为2002::/16 其中如何结合ipv4地址? 2002:i ...
- Http GET 请求参数中文乱码
两种解决方式 第1种:代码里转换 String name = request.getParamter("name"); String nameUtf8 = new String(n ...
- Windows10 office 点击链接提示您的组策略阻止我们为您完成此操作。设置ChromeHTML也无效.
问题: win10环境点击office 中的网络链接时 出现了如下报错(一般在卸载了系统预装的其他浏览器后出现问题) 解决方案: 1.设置默认浏览器(已经设置可跳过) 控制面板->设置小图标 ...
- CA周记 - 带你进⼊ OpenAI 的世界
2021年11月的 Microsoft Ignite , 微软带来了全新的 Azure OpenAI Service,通过新的 Azure 认知服务能够访问 OpenAI 强大的 GPT-3 模型 . ...