数位dp入门 HDU 2089 HDU 3555
最基本的一类数位dp题,题目大意一般是在a~b的范围,满足某些要求的数字有多少个,而这些要求一般都是要包含或者不包含某些数字,或者一些带着数字性质的要求,一般来说暴力是可以解决这一类问题,可是当范围非常大时,暴力明显会超时,这时便是需要把它转化为一类dp问题,这就是数位dp。像一个数1234567890,直接把它暴力的话,那它就是一个1e9级别的数,可是把它一个数位一个数位的话,那它就是1位是0,2为是9,一直到10位是1,一共才10位的长度,明显就比暴力明朗多了。
由于我也是刚接触不久,还没有什么自己深刻感悟,日后再做总结,现在先具体问题具体分析,来两道入门的数位dp题进行进行理解
不要62和4HDU - 2089
题目大意就是,一个数字它如果含有62或者4它就是不吉利数字,而你要求的就是【n,m】范围内的不含有不吉利数字的个数。
这题的范围只有1e6,所以暴力还是行得通的,不过我们在学数位dp,就用数位dp来解决。那我们想一下,每一个数个位,十位,百位。。。的范围都是从0~9,那么我们把一个数拆成一位位,例如,一个万位的数就可以拆成
万:0 1 2 3 4 5 6 7 8 9
千:0 1 2 3 4 5 6 7 8 9
百:0 1 2 3 4 5 6 7 8 9
十:0 1 2 3 4 5 6 7 8 9
个:0 1 2 3 4 5 6 7 8 9
现在假如一个数的10086,那它的范围内有多少个不含不吉利吉利的数呢,首先我们得把10086分解,得出一个上界,类似这个样子
万:0 1 2 3 4 5 6 7 8 9 上界1
千:0 1 2 3 4 5 6 7 8 9 上界0
百:0 1 2 3 4 5 6 7 8 9 上界0
十:0 1 2 3 4 5 6 7 8 9 上界8
个:0 1 2 3 4 5 6 7 8 9 上界6
这个上界就是在有限制的情况下,当前数位的数字选择只能从[0,上界]中选择,那么这个限制又是什么吗,其实就是范围限制,就像现在我们要求的是10086范围内的,那么如果万位取0,对千位肯定就没有限制可以取[0,9]中任意的数字,但当万位取1,千位就有限制了只能取到0,要是取到1那么就不是求10086范围内的了,而是求11xxx范围内的了,那这个限制怎么传递呢,首先当前数位如果没有限制的话,那么对下面的数位也没有限制,像万位取0,下面的数位都没有限制,而当前数位如果有限制的话,并且取的是上界的话,对下面的数位就有限制,像万位取1,千位有限制,然后千位取0是上界又对下面的又有限制,而像万位取1,千位取0,百位取0,对十位有限制,但十位取小于8的数对个位就没有限制,所以上界的限制传递的条件就是,当前数位有限制并且取的是上界,就会对下面的数位有限制
那现在回到问题,怎么确定不是不吉利的数有几个呢,首先每一位肯定是不能含有4这个数字的,然后就是上一数位是6,当前数位就不能是2,详情见代码
#include<cstdio>
#include<cstring>
int n,m,up[],dp[][];//dp[i][j]数位i的上一数字是不是6时不含不吉利的数字有多少个
int dfs(int p,bool is6,bool isu)//p当前数位,is6上一个数位是不是6,isu当前数位有没有限制
{
if(p==)//边缘值,p为0是个空集,肯定没有62或4,返回1
return ;
if(!isu&&dp[p][is6]!=-)
return dp[p][is6];
int ans=;
for(int i=;i<=(isu ? up[p] : );i++)
{
if((is6&&i==)||i==)//当前数位不能取4,以及如果上一数位取6,当前数位不能取9
continue;
ans+=dfs(p-,i==,isu&&i==up[p]);//继续往下判断下一数位
}
if(!isu)
dp[p][is6]=ans;
return ans;
}
int solve(int x)
{
int num=;
while(x)
{
up[++num]=x%;
x/=;
}
return dfs(num,,);
}
int main()
{
memset(dp,-,sizeof(dp));
while(scanf("%d%d",&n,&m)&&(n||m))
printf("%d\n",solve(m)-solve(n-));
return ;
}
666
关于记忆化,当前数位没有限制时,那么它取任何数相对应的结果是通用的,记忆化搜索就达到了省时的效果
要49HDU - 3555
相比上一题不要62和4,这题就是给出一个n,要求[1,n]范围内含49的数字有多少个,不过我们可以反过来求出不含49的,然后减去即可
#include<cstdio>
#include<cstring>
#define ll long long
ll n,dp[][];
int t,up[];
ll dfs(int p,bool is4,bool isu)
{
if(p==)
return ;
if(!isu&&dp[p][is4]!=-)
return dp[p][is4];
ll ans=;
for(int i=;i<=(isu ? up[p] : );i++)
{
if(is4&&i==)
continue;
ans+=dfs(p-,i==,isu&&i==up[p]);
}
if(!isu)
dp[p][is4]=ans;
return ans;
}
ll solve(ll x)
{
int num=;
while(x)
{
up[++num]=x%;
x/=;
}
return dfs(num,,);
}
int main()
{
scanf("%d",&t);
memset(dp,-,sizeof(dp));
while(t--)
{
scanf("%lld",&n);
printf("%lld\n",n+-solve(n));
}
return ;
}
999
对于答案的+1,因为我们的数位dp求的是[0,n]范围内的不含49的数字的个数,而题目要求的是[1,n]范围内含49的数字的个数,直接用n减去的话,0肯定不含49,会多减去一个0,所以要+1
当然也有直接就是求[1,n]范围内含49的数字个数的,详情见代码
#include<cstdio>
#include<cstring>
#define ll long long
ll n,dp[][],cf[]={};//cf保存10的次方
int t,up[];
ll dfs(int p,bool is4,bool isu)
{
if(p==)//空集不含49,返回0
return ;
if(!isu&&dp[p][is4]!=-)
return dp[p][is4];
ll ans=;
for(int i=;i<=(isu ? up[p] : );i++)
{
if(is4&&i==)//上一数位的4,当前位是9,可以计算答案了
ans+=isu ? n%cf[p-]+ : cf[p-];//有上界的限制,比如n是49870,
//当前位是第4位取9,那么49000~49870都是可以的,也就是n%1000+1
//反之没有限制的话,比如n是50000,当第5位是4,当前位是第4位取9
//那么49000~49999都是可以的,也就是1000
else
ans+=dfs(p-,i==,isu&&i==up[p]);
}
if(!isu)
dp[p][is4]=ans;
return ans;
}
ll solve(ll x)
{
int num=;
while(x)
{
up[++num]=x%;
x/=;
}
return dfs(num,,);
}
int main()
{
for(ll i=;i<=;i++)
cf[i]=cf[i-]*;
scanf("%d",&t);
memset(dp,-,sizeof(dp));
while(t--)
{
scanf("%lld",&n);
printf("%lld\n",solve(n));
}
return ;
}
999
数位dp入门 HDU 2089 HDU 3555的更多相关文章
- HDU 2089 不要62【数位DP入门题】
不要62 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- hdu3555 Bomb 数位DP入门
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555 简单的数位DP入门题目 思路和hdu2089基本一样 直接贴代码了,代码里有详细的注释 代码: ...
- xbz分组题B 吉利数字 数位dp入门
B吉利数字时限:1s [题目描述]算卦大湿biboyouyun最近得出一个神奇的结论,如果一个数字,它的各个数位相加能够被10整除,则称它为吉利数.现在叫你计算某个区间内有多少个吉利数字. [输入]第 ...
- 数位dp入门 hdu2089 不要62
数位dp入门 hdu2089 不要62 题意: 给定一个区间[n,m] (0< n ≤ m<1000000),找出不含4和'62'的数的个数 (ps:开始以为直接暴力可以..貌似可以,但是 ...
- 【数位dp】【HDU 3555】【HDU 2089】数位DP入门题
[HDU 3555]原题直通车: 代码: // 31MS 900K 909 B G++ #include<iostream> #include<cstdio> #includ ...
- 递推、数位DP解析(以HDU 2089 和 HDU 3555 为例)
HDU 2089 不要62 题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2089 Problem Description 杭州人称那些傻乎乎粘嗒嗒的人 ...
- HDU 2089 - 不要62 - [数位DP][入门题]
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089 Time Limit: 1000/1000 MS (Java/Others) Memory Li ...
- Hdu 2089 不要62 (数位dp入门题目)
题目链接: Hdu 2089 不要62 题目描述: 给一个区间 [L, R] ,问区间内不含有4和62的数字有多少个? 解题思路: 以前也做过这个题目,但是空间复杂度是n.如果数据范围太大就GG了.今 ...
- hdu:2089 ( 数位dp入门+模板)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089 数位dp的模板题,统计一个区间内不含62的数字个数和不含4的数字个数,直接拿数位dp的板子敲就行 ...
随机推荐
- JavaScript-checkbox标签-隐藏、显示、全选、取消和反选等操作
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 搞懂Dubbo SPI可拓展机制
前言 阅读本文需要具备java spi的基础,本文不讲java spi,please google it. 一.Dubbo SPI 简介 SPI(Service Provider Interface) ...
- 链表中环的入口结点——牛客剑指offer
题目描述: 给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null. 题目分析: 从上图中可以看出,环的入口结点和其他结点的区别:环的入口结点是有两个指针指向的,其他结点除了头结点都 ...
- pg_ctl — 启动、停止、重启 PostgreSQL
pg_ctl 名称 pg_ctl -- 启动.停止.重启 PostgreSQL 语法 pg_ctl start [-w] [-s] [-D datadir] [-l filename] [-o opt ...
- Postgresql在Windows下的解压安装
1.将下载的压缩包解压,我是解压在D:\postgreSQL\pgsql中. 2.设置环境变量如下: set PGHOME=D:\postgreSQL\pgsql set PGDATA=%PGH ...
- ArrayList,LinkedList,Vector区别.TreeSet,TreeSet,LinkedHashSet区别
ArrayList: 基于数组的数据结构,地址连续,一旦数据保存好了,查询效率比较高,但是因为其地址连续,所以增删数据需要移动数据,影响速度 内部数组长度默认为10,当需要扩容时,数组长度按1.5倍增 ...
- Scala学习十——特质
一.本章要点 类可以实现任意数量的特质 特质可以要求实现它们的类具备特定的字段,方法或超类 和Java接口不同,Scala特质可以提供方法和字段实现 当你将多个特质叠加在一起时,顺序很重要——其方法先 ...
- StoneTab标签页CAD插件 2.6.0
1.纯属自娱自乐,未做过多的测试: 2.理论上可以用在CAD2010-2012版本,自己用的是WIN10 64位,CAD2012,其他未过测试: 3.尚未打算支持其他版本CAD,主要是电脑只能装WIN ...
- 向PHP发送HTTP-Post请求
欢迎访问我的个人博客,获取更多有用的东西 链接一 链接二 也可以关注我的微信订阅号:CN丶Moti 1.post.html <!DOCTYPE html> <html lang=&q ...
- Centos7:Redis3.0集群搭建
Redis集群中至少应该有三个节点.要保证集群的高可用,需要每个节点有一个备份机.Redis集群至少需要6台服务器. 搭建伪分布式.可以使用一台虚拟机运行6个redis实例. 修改redis的端口号7 ...