题目描述:

杭州人称那些傻乎乎粘嗒嗒的人为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. Fiddler 抓包设置

    手机抓包设置 一:配置Fiddler参数 打开Fiddler菜单项Tools->TelerikFiddler Options->HTTPS, 勾选CaptureHTTPS CONNECTs ...

  2. c++ explicit 构造函数

    代码 #include<iostream> using namespace std; class Example { private: int data; public: Example( ...

  3. pyspider和pyquery总结

    1.参考 pyspider作者官网: pyspider 爬虫教程(一):HTML 和 CSS 选择器 pyspider 爬虫教程(二):AJAX 和 HTTP pyspider 爬虫教程(三):使用 ...

  4. PID控制算法的简单分析和仿真!

    PID算法简单剖析如下: 1.首先我们来看一下PID系统的基本组成模块: 如图所示,图中相关参数的表示如下: r(t):系统实际上需要的输出值,这是一个标准值,在我们设定了之后让这个系统去逼近的一个值 ...

  5. Spark环境搭建(三)-----------yarn环境搭建及测试作业提交

    配置好HDFS之后,接下来配置单节点的yarn环境 1,修改配置文件 文件 : /root/app/hadoop-2.6.0-cdh5.7.0/etc/hadoop/yarn-site-xml 插入 ...

  6. 07-Python入门学习-字符编码与文件处理

    字符编码 人操作计算机使用人类认识的字符,而计算机存放都是二进制数字所以人在往计算机里输入内容的时候,必然发生: 人类的字符------(字符编码表)-------->数字 比如我输入一个‘上’ ...

  7. 10树莓派Samba的安装与配置

    2017-08-31 12:28:26 1.安装samba服务打开终端或者SSH连接树莓派,输入如下命令: sudo apt-get install samba 已经安装过了显示下列信息: pi@ra ...

  8. 通用导出excel

    循环导出所有行和列 def export_excel(table_name): host,user,passwd,db='192.168.0.12','root','myjcyf','us_sys' ...

  9. 关于MDN,HTML入门来自MDN文档

    由开发者和作者组成的开源社区:推动web发展, MDN(Mozilla Developer Network) 维基,共同维护做贡献: 需要使用到github账号进行验证,以此再创建MDN账号: HTM ...

  10. __x__(34)0908第五天__ 定位 position

    position 定位 指将原始摆放到页面的任意位置. 继承性:no 默认值:static        没有定位,原始出现在正常的文档流中 可选值: static :    默认值,元素没有开启定位 ...