hdu 2089 不要62 (数位DP)

hdu  2089 不要62  (数位DP)



不要62

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 26005    Accepted Submission(s): 9054


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
 

Author
qianneng
 

Source
 

Recommend
lcy   |   We have carefully selected several similar problems for you:   2082  2079  2065  2078  1018 
 



题意:这是很好懂的中文题,就不解释了


题解:
dp[len][0] 表示在长度为len时,不存在62和4的数的数量
dp[len][1] 表示在长度为len时,开头,也就是dp[len][1] == 2,并且不存在62和4的数的数量
dp[len][2] 表示在长度为len时,存在62和4的数的数量
数位DP,先打表,求出不同长度下的含有特殊数字的数的个数有多少,然后开始一步一步往下求
也就是22345
这样,先20000之内的
然后2000
然后300
然后40

这样,同时还要考虑会不会之前就存在了特殊数字



在代码中有详细的讲解



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <iostream>

using namespace std;

__int64 dp[25][3];
// dp[len][0] 表示在长度为len时,不存在62和4的数的数量
// dp[len][1] 表示在长度为len时,开头,也就是dp[len][1] == 2,并且不存在62和4的数的数量
// dp[len][2] 表示在长度为len时,存在62和4的数的数量

__int64 digit[25];      //存放每个位数上的数字	

void Init (){             //进行dp[][]的预处理
	int i;

	memset (dp, 0, sizeof (dp));
	dp[0][0] = 1;
	for (i = 1; i < 20; ++i){
		dp[i][0] = dp[i - 1][0] * 9 - dp[i - 1][1];    //它原先就是不含特殊数字,所以再加一位,就乘10,因为有可能有4,所以乘9,又因为有可能原先2开头,所以减去dp[i - 1][1]
		dp[i][1] = dp[i - 1][0];       //直接是没有特殊数字的dp[i - 1][0]就可以了,前面加上2,就好了
		dp[i][2] = dp[i - 1][2] * 10 + dp[i - 1][0] + dp[i - 1][1];   //前面加什么都可以,直接先乘10,然后加上首位加4的一种情况,再加上首位是6前一位是2的情况
	}
}

int solve(int n){        //处理数字,同时要注意,这里并不会判断n这个数字,只会是n之前的所有数字
	int k = 1, i;
	int temp = n;

	memset (digit, 0, sizeof (digit));            //要记得消除,不然会出现数据错误
	while (n){           //将位数一个个提取出来
		digit[k++] = n % 10;
		n /= 10;
	}
	int ans = 0, flag = 0;
	for (i = k - 1; i >= 1; --i){
		ans += dp[i - 1][2] * digit[i];          //先加上所有的包含特殊数字的dp[i - 1][2]乘这个数位
		if (flag) 
			ans += dp[i - 1][0] * digit[i];       //如果前面已经是特殊情况了,那么就将剩下的所有都慢慢加上去
		if (!flag && digit[i] > 4)
			ans += dp[i - 1][0];                 //如果这个位数上可以是4,那么就加上后一位不含特殊数字的数
		if (!flag && digit[i] > 6)
			ans += dp[i - 1][1];                    //如果这个位数上可以有6,那么就加上后一位是2开头的数
		if (!flag && digit[i + 1] == 6 && digit[i] > 2)
			ans += dp[i][1];                               //这是在判断特殊情况,就是个位上的数字,用来给出范围,    很重要的东西      
		if (digit[i] == 4 || (digit[i + 1] == 6 && digit[i] == 2)) 
			flag = 1;                                                    //判断是不是构成了特殊情况,就是前面的数字已经含有了特殊情况
	} 
	return temp - ans;            
}

int main (){
	int n, m;

	Init ();
	while (~scanf ("%d%d", &n, &m), n + m){
		printf ("%d\n", solve(m + 1) - solve(n));
	}
		
	return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值