不要62
Problem Description
杭州人称那些傻乎乎粘嗒嗒的人为62(音:laoer)。 杭州交通管理局经常会扩充一些的士车牌照,新近出来一个好消息,以后上牌照,不再含有不吉利的数字了,这样一来,就可以消除个别的士司机和乘客的心理障碍,更安全地服务大众。 不吉利的数字为所有含有4或62的号码。例如: 62315 73418 88914 都属于不吉利号码。但是,61152虽然含有6和2,但不是62连号,所以不属于不吉利数字之列。 你的任务是,对于每次给出的一个牌照区间号,推断出交管局今次又要实际上给多少辆新的士车上牌照了。
Input
输入的都是整数对n、m(0<n≤m<1000000),如果遇到都是0的整数对,则输入结束。
Output
对于每个整数对,输出一个不含有不吉利数字的统计个数,该数值占一行位置。
Sample Input
1 100 0 0
Sample Output
80
解题思路
数位dp,ural上有一个更简单的数位dp的题1009. K-based Numbers,我博客中也有题解http://blog.csdn.net/ly59782/article/details/53328063,可以先看看ural上简单一点的那个题目。,
同样的,以什么为结尾并不好思考,我们还是考虑以什么为开头,然后不断往高位插入数。
那么dp[i][j] 就表示前 i 位以 j 打头的合格数
因为不能有4 出现, 所以 dp[i][4] = 0
那么 dp[i][j] = ∑ dp[i-1][k] ( j!= 6 || k != 2)
这样我们就有了 0~9,10~19,20~29,........,100~199,200~299…………这些区间内的合格数了, 但是由于我们要求的区间不是这种简单的区间,例如要求10~235区间内合格数目,显然是 先求出0~235,0~9,然后相减。那么怎么处理像0~235这种区间呢
先求出0打头的三位数0XX(即从001~099)+
然后求1打头的三位数1XX(即从100~199)+
2打头的不考虑
这样我们就有了从0~199范围内的合格数了,然后从200~235 其实就相当于从0~35这个范围。
下面求0~35
0打头的两位数 0X+
1打头的两位数1X+
2打头的两位数2X
3打头的不考虑
这样我们0~199,200~229范围内的都有了,最后还剩下 30~35,也就相当于求0~5这个区间,同样的
0打头的1位数+1打头的1位数……
5打头的不考虑
这里出现了问题, 5打头的不考虑不是少了一个数吗?由于前面都不考虑,这里为了简化程序,不做特判,所以也不考虑,但是不能让答案少一个数呀? 所以我们传区间的时候可以+1,求0~235相当于求了0~234,那么相求0~235,传参传0~236就搞定了
代码
#include <iostream> #include <cstdio> #include <map> #include <cmath> #include <string.h> #include <algorithm> #include <set> #include <sstream> using namespace std; const int maxn = 0; const int INF = 0x3f3f3f3f; int dp[8][10]; int d[8]; int n,m; int f(int n){ int ans = 0; int len = 0; ///化成字符串形式,并倒置存放 while(n){ d[++len] = n; n /= 10; } d[len+1] = 0;///避免上次结果影响 for(int i = len; i>=1;--i){ for(int j = 0; j<d[i]; ++j){ if(d[i+1]!= 6 || j!=2) ans += dp[i][j]; } if(d[i]==4 || (d[i+1] ==6 && d[i]==2)) break; } return ans; } int main() { ///初始化dp dp[0][0] = 1; for(int i = 1 ;i<=7;++i){ for(int j = 0 ;j<=9;++j) for(int k = 0; k<=9; ++k) if(j!=4 && !(j==6&&k==2)) dp[i][j] += dp[i-1][k]; } while(scanf("%d%d",&n,&m)!=EOF && !(n==0 && m==0)){ printf("%d\n",f(m+1)-f(n)); } return 0; }