HDUOJ 2089 数位DP

不要62

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


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
 
人生第一道数位DP,看着别人的代码写的,几乎是用抄的,但虽然这样,感觉还是增加了我对数位DP的理解。
 
其他地方网上各种题解都讲的很多,我只讲自己学到的东西。
 
要求从n到m的数中一共有多少个满足条件的,通常将这类问题拆成两个子问题,就是算出从0到n中有多少个满足条件的,再算出从0到m中有多少个满足条件的,最后将两个数一减即可,而这两个子问题都有相同的结构,处理办法都相同。
 
预处理的过程是计算出前i位数中满足条件的有几个,这里的前i位数可以有前导零,因为在后续的处理中,用到预处理结果的地方都不是最高位(比如说预处理结果存在数组dp[i][j]中,i表示位数,一般在处理最高位时调用的都是dp[i-1][j])。预处理的转移方程一般都是比较好确定的。
 
难得是后续处理,比如对于一个数4567,从它的最高位依次对它处理,第一位是4,通过预处理的结果可以知道0~3999中有多少个满足条件的数(当然可能还要经过其他一系列讨论),下一步处理它的下一位5,这次操作可以知道从4000~4499中有多少个满足条件的数,再下一步,能确定4500到4559中有多少满足条件的数,再下一步,4560~4566.9(这里的0.9就不会继续处理了)所以对一个数n,处理的结果是0~n(不包括n)中有多少满足条件的数,而不是0~n(包括n)中有多少满足条件的数。
 
 
代码:
#include <iostream>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <cstring>


#define MAXN 1000010
#define INF 2100000000
#define esp 1e-6
using namespace std;

long long dp[10][3];
int n, m;
long long bit[10];
long long solve(long long x)
{
        int cnt = 0;
        long long  res = 0;
        long long temp = x;
        bool flag = false;
        while(temp != 0)
        {
                bit[++cnt] = temp%10;
                temp /= 10;
        }

        bit[cnt+1] = 0;

        for(int i = cnt; i > 0; i--)
        {
                res += dp[i-1][2]*bit[i];

                if(flag) res += dp[i-1][0]*bit[i];

                if(!flag && bit[i] > 4) res += dp[i-1][0];

                if(!flag && bit[i+1] == 6 && bit[i] > 2) res += dp[i][1];

                if(!flag && bit[i] > 6) res += dp[i-1][1];

                if(!flag && (bit[i] == 4 || (bit[i+1] == 6 && bit[i] == 2)))  flag = true;
        }

        return x-res;
}
int main()
{
	//freopen("C:/Users/Admin/Desktop/in.txt", "r", stdin);
	memset(dp, 0, sizeof(dp));
        dp[0][0] = 1;

	for(int i = 1; i < 10; i++)
        {
                dp[i][0] = dp[i-1][0]*9 - dp[i-1][1];
                dp[i][1] = dp[i-1][0];
                dp[i][2] = dp[i-1][2]*10 + dp[i-1][1] + dp[i-1][0];
        }

        while(cin >> n >> m)
        {
                if(n == 0 && m == 0) return 0;
                long long a = solve(m+1) ;
                long long b = solve(n);

               printf("%lld\n", a-b);
        }

        return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值