题目描述:

杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。
杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。
不吉利的数字为所有含有4或62的号码。例如:
62315
73418
88914
都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。
你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。

输入:

输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。

输出:

对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。

 题目大意:

给出左区间n,右区间m,求该区间不含62以及4的数有多少个

 题目分析:

  第一种方法:当我看到这道题的第一个思路就是暴力过,判断一个数中是否含有连续的62,以及单个4, 对于给定的任意一个数x,我们都可以求出它各个位上的数, 复杂度为O(n),因此可以打表暴力过。

    这种方法比较简单。

#include<stdio.h>

int num[1000000 + 10] = {0}; //打表记录 1 到 1000000 前i个数中吉利的数字有多少个
int n, m; int check(int x)
{
while(x)
{
if(x % 10 == 4 || x % 100 == 62) //不吉利
return 0;
x /= 10; //每位都要判断
}
return 1; //非不吉利数 返回 1, 贡献+1
} int main()
{
for(int i = 1; i <= 1000000; i ++)
num[i] = num[i - 1] + check(i); //前缀和思想, 前i个非不吉利数为前 i - 1个已经记录的 + check(i) 的贡献
while(scanf("%d%d", &n, &m)!=EOF)
{
if(n == 0 && m == 0)
break;
printf("%d\n", num[m] - num[n - 1]);
}
return 0;
}

  第二种方法:

   暴力思想很重要但是大多时候都会受到时间限制,我第一次AC这道题是运用了数位dp, 数位dp是很灵活的方法, 我对它也运用的不熟练, 只掌握一道题也形成不了系统, 要多练

      数字与它每一位的关系, 知道一个数字n, 我们便可以通过对它 进行 对10取模再整除, 可以依次得到各个位置上的数,以及n的长度len,用数位dp的话我们需要用到 数的长度 以及 各个位置上的数值

int length(int n)
{
int len=;
while(n)
{
len++;
n/=;
}
return len;
} // 该数值的长度
void caldigit(int n,int len)
{
memset(digit,,sizeof(digit));
for(int i=;i<=len;i++)
{
digit[i]=n%;
n/=;
}
} // 该数值各个位的数

     简单了解一下数位dp,数位DP一般应用于求出在给定区间[A,B]内,符合条件P(i)的数i的个数,条件P(i)一般与数的大小无关,而与数的组成有关。

     因此我们可以定义二维数组dp[i][j],代表以j为最高位的i位数,在看下面的话以及代码的时候要时刻记住dp所代表的含义,

   例如, dp[ 2 ][ 3 ]代表以3为最高位的2位数, 即30~39 ,dp[ 5 ][ 4 ]代表以4为最高位的5位数,即40000~49999。

   但是这样只是为dp[][]赋了一个区间的意义, 我们还需要给它赋上一个核心意义, 就是在该dp[][]所代表的区间中所含的非不吉利数,因此我们更新二维数组dp[i][j],代表以j为最高位的i位数中非不吉利数的个数为多少个。

  然后用一个count(int n)计算1~n之间非不吉利数的数目, 则n~m之间非不吉利数可以表示为count(m) - count(n - 1)

例如count()结果为 dp[][] + dp[][] + dp[][] + dp[][] + dp[][] + dp[][] + dp[][] + dp[][] + dp[][]

这里要说明一下,认为009是长度为3,首位为0的符合条件的数,09是长度为2首位为0符合条件的数

在上面的代码中展示了 我们用digit[]数组记录了n的各个位数上的值, 也就是说从高位往地位处理才是正确的方法, 这样可以优化, 当高位为4或者62时便可以停止count了, 不再计算低位的dp值

例如3456,我们先是dp[][]即 ~

然后加上dp[][] + dp[][](不再列举, 参考上面) + dp[][] + ... +当加到dp[][]时我们就应该停止计算了

因为digit[] == ,接下来计算的值将是401~456可以知道这都包含4,全为不吉利数字,停止往地位继续计算,同理自己要去想想62的情况,在草稿纸上模拟一下就很容易理解了

  最后一点便是这里需要做一个dp预处理, 因为我们是在题目给定的范围1~1000000中多次询问一个区间, 为了避免每次询问都要重新记录dp增加时间复杂度, 我们可以进行dp的预处理,将1~1000000中各个区间的不吉利数先记录下来,然后在查询的时候便可以直接调用相应的值来进行相加计算.

  当时是自己手动模拟一遍才理解的过程, 讲的不好还得自己去模拟一遍才行

#include<stdio.h>
#include<string.h>
int digit[10];
int dp[10][10];
int length(int n)
{
int len=0;
while(n)
{
len++;
n/=10;
}
return len;
} // 该数值的长度
void caldigit(int n,int len)
{
memset(digit,0,sizeof(digit));
for(int i=1;i<=len;i++)
{
digit[i]=n%10;
n/=10;
}
} // 该数值各个位的数
int count(int n)
{
int ans=0;
int len=length(n);
caldigit(n,len);
for(int i=len;i>=1;i--)//从高位开始
{
for(int j=0;j<digit[i];j++)//枚举该位上的每一位数字
{
if(j!=4&&!(j==2&&digit[i+1]==6))
ans+=dp[i][j];
}
if(digit[i]==4||(digit[i]==2&&digit[i+1]==6))//倘若该位上为62或者4,就不必继续枚举,因为后面的数一定不符合,例如452,453,624
break;
}
return ans;
}
int main()
{
dp[0][0]=1;
for(int i=1;i<=7;i++)//对dp做预处理
{
for(int j=0;j<10;j++) //第i位上的数
{
for(int k=0;k<10;k++)//第 i - 1位上的数
{
if(!(j==4)&&!(j==6&&k==2)) //不为4或者62才能算一个贡献
dp[i][j]+=dp[i-1][k];
}
}
}
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0)
break;
printf("%d\n",count(m+1)-count(n));
}
return 0;
}

  

第三种方法:

  最后一种方法是听学长讲的, 说实话没用过这种方法,数位dp + 记忆化搜索, 其实动态规划与记忆化搜索有异曲同工之处, 两者都是有一个记录的过程, 记录了上一步的值,在进行下一步的时候便可以直接调用。

   这里参考了一下别人的解释:

数组dp[][]的元素初始化为-1,表示其值尚未计算得到,需要用函数dfs()进行计算。初始化应该放在主函数中循环处理之前进行,可以最大限度避免重复计算。

   函数count(n)的功能是计算(0,n]的满足条件的数的个数。做法是将n的各位分解成数字位0-9,放入数组digits[]中,个位放在digits[0]中,即低位放在下标小的数组元素中,高位放在下标大的数组元素中。然后通过深度优先搜索函数dfs(),根据数组digits[]指定的数去搜索。

   这里的dp[][]与上面的数位dp含义略不同,这里是指第i位的前一位为j时,非不吉利数的多少

  

/*
* 参数:
* pos - 数位位置,即当前处理数的第几位,从高位开始
* pre - 前导,即前一位数字
* limit - 是否为数位上界(最大数字)
*/

  

#include<stdio.h>
#include<string.h> int n, m;
int dp[20][10], digit[8]; int dfs(int pos, int pre, int limit)
{
if(pos == 0)// 递归边界,已经枚举结束,则1个数满足条件
return 1;
if(!limit && dp[pos][pre] != -1) // 已经搜索过的不再搜索,直接使用之前的计算结果
return dp[pos][pre];
int ans = 0;
int maxd = limit ? digit[pos] : 9;
for(int i = 0; i <= maxd; i ++)
{
if(i == 4 || (i == 2 && pre == 6)) // 枚举数字,如果数字不同则枚举0-9
continue;
ans += dfs(pos - 1, i, limit && i == digit[pos]);
}
if(!limit)
dp[pos][pre] = ans;
return ans;
} int count(int n)
{
int len = 0;
while(n)
{
digit[++ len] = n % 10;
n /= 10;
}
return dfs(len, 0, 1);
} int main()
{
memset(dp, -1, sizeof(dp));
while(scanf("%d%d", &n, &m)!=EOF)
{
if(n == 0 && m == 0)
break;
printf("%d\n", count(m) - count(n - 1));
}
return 0;
}

  

HDU 2089 不要62【解题报告】的更多相关文章

  1. Hdu 2089 不要62 (数位dp入门题目)

    题目链接: Hdu 2089 不要62 题目描述: 给一个区间 [L, R] ,问区间内不含有4和62的数字有多少个? 解题思路: 以前也做过这个题目,但是空间复杂度是n.如果数据范围太大就GG了.今 ...

  2. HDU 4303 Hourai Jeweled 解题报告

    HDU 4303 Hourai Jeweled 解题报告 评测地址: http://acm.hdu.edu.cn/showproblem.php?pid=4303 评测地址: https://xoj. ...

  3. hdu 2089 不要62【数位dp】

    HDU 2089 求给定区间内不含62和4的数的个数. 数位dp入门.从这里我清楚了一些数位dp的用法.比如limit是判断是否达到上界,而且需要判断(!limit)..比如若题目要求不含11的个数, ...

  4. 数位DP HDU - 2089 不要62

    不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  5. hdu 2089 不要62(初学数位DP)

    http://acm.hdu.edu.cn/showproblem.php?pid=2089 题意: 给定 m,.n; 求车牌号 m~n之间 有多少数字 不含 4或62     ,8652是可以的 . ...

  6. HDU 2089 - 不要62 - [数位DP][入门题]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089 Time Limit: 1000/1000 MS (Java/Others) Memory Li ...

  7. HDU 2089 不要62(数位dp模板题)

    http://acm.hdu.edu.cn/showproblem.php?pid=2089 题意:求区间内不包含4和连续62的数的个数. 思路: 简单的数位dp模板题.给大家推荐一个好的讲解博客.h ...

  8. [hdu 2089] 不要62 数位dp|dfs 入门

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089 题意:求[n, m]区间内不含4和62的数字个数. 这题有两种思路,直接数位dp和dfs 数位d ...

  9. HDU 2089 不要62:数位dp

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2089 题意: 问你在区间[n,m]中,有多少个数字不含"4"且不含"62 ...

随机推荐

  1. Windows caffe 跑mnist实例

       一. 装完caffe当然要来跑跑自带的demo,在examples文件夹下. 先来试试用于手写数字识别的mnist,在 examples/mnist/ 下有需要的代码文件,但是没有图像库. mn ...

  2. web开篇

    一.内容回顾 1.python基础 2.网络编程 3.并发编程 4.前端 5.数据库(MySQL) 二.今日概要 1.了解Web应用程序的本质 2.Django简介及安装使用 三.今日详细 1.最简单 ...

  3. 使用FLASK+winscp在服务器端发布一个表白网页

    本地环境采用Windows 环境下Pycharm开发环境.

  4. MySQL学习笔记:timediff、timestampdiff、datediff

    一.时间差函数:timestampdiff 语法:timestampdiff(interval, datetime1,datetime2) 结果:返回(时间2-时间1)的时间差,结果单位由interv ...

  5. Zabbix(一)

    安装一台CentOS: CentOS-7.4-x86_64-DVD-1708 https://www.cnblogs.com/xiewenming/p/7732144.html https://blo ...

  6. 经典合集 - WP8.1数据源

    [内容说明:] 这里是一个页面类型的数据源,在开发WP应用程序中,因为目前没有数据库服务器等网络资源,突发奇想,通过博客园提供的WCF接口访问该博文地址获取文章内容,在应用程序中使用正则表达式解析并转 ...

  7. Java Spring Boot VS .NetCore (四)数据库操作 Spring Data JPA vs EFCore

    Java Spring Boot VS .NetCore (一)来一个简单的 Hello World Java Spring Boot VS .NetCore (二)实现一个过滤器Filter Jav ...

  8. python数据类型——数据转换

    数据类型有很多种,比如数值和字符,比如6和a,字符是需要加双引号的,下面的例子运行的结果是不一样的,数值会相加而字符会相连 print(6+6)print("6"+"6& ...

  9. CodeForces 553E Kyoya and Train 动态规划 多项式 FFT 分治

    原文链接http://www.cnblogs.com/zhouzhendong/p/8847145.html 题目传送门 - CodeForces 553E 题意 一个有$n$个节点$m$条边的有向图 ...

  10. lArea.js 城市选择

    http://blog.csdn.net/libin_1/article/details/50689075 lArea.js