HDU 2089 不要62

不要62

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


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 1000 0
 

Sample Output
 
 
80
 

Author
qianneng


题解:久闻“数位dp”这一名词许久,今日终于有所一见,果然是威力非凡。
中文题意不解释,这道题有两种解法。
法一:暴力预处理+遍历
最大的时间复杂度就在预处理里了。
贪图方便直接把数组转string然后find查找是否有4或者62的字串然后标记就可以了。
法二:数位dp。所谓dp就是我们把一些无后效性的东西算出来以后就存下来。
那么关键是找到一个状态,这里定义f[i][j]为i位数中最高位为j的满足条件的数的个数。
比如f[1][2]=1; f[1][4]=0; f[2][6]=8 (60,61,63,64,65,66,67,68,69).
首先f[1][i]=0(if i==4),f[1][i]=1(if i!=4) (0<=i<=9)。这一步是很显然的,那么根据这个题的数据范围,只需要递推到f[7][i]就够用了。那么稍微理解一下,可以想出递推式:
  f[i][j]=
    if (j==4) f[i][j]=0
    else if (j!=6) f[i][j]=Σf[i-1][k] (k=0,1,2,3,4,5,6,7,8,9)
    else if (j==6) f[i][j]=Σf[i-1][k] (k=0,1,3,4,5,6,7,8,9)
上面的式子也是很显然的,如果觉得不显然可以这样想:i位数,最高位是j的符合条件的数,如果j是4,肯定都不符合条件(因为题目不让有4),所以直接是0;如果j不是6,那么它后面随便取,只要符合题意就可以,所以是f[i-1][k],k可以随便取的和;如果j是6,后面只要不是2就行,所以是f[i-1][k],k除了2都可以,求和。
这里要说明一下,认为00052是长度为5,首位为0的符合条件的数,052是长度为3首位为0符合条件的数。
当我们得到这一切以后我们要怎么计算小于m的数的个数呢
我们这样做
对于一个数首先将每一位都存下来,然后按最高位、次高位、...最低位进行处理
举个栗子:对于数字3420,我们首先计算位数为4的最高位为0、1、2的所有满足条件的数的个数(之前已经算出来了)
并把它们累加起来,那么其实这样我们已经算出来了0~3000中所有的数的个数。
接下来对与3000~3420其实我们只要计算0~420就可以了
所以我们计算0~99、100~199、200~299、300~399
那么我们发现当我们计算到这里的时候还需不需要继续计算呢,不用了。
因为后面要计算的是400~420(实际上是3400~3420)因为有一个4,所以后面的都不可能了,直接break
所以我们发现当数字出现4或者62时需要进行break
那么62的处理方式是:如果前一个位数是6当前处理数是2,那么break
举栗:362,先计算0~99、100~199、200~299
                 再次0~9、10~19、20~29、30~39、40~49、50~59
                 接下来就不用算了。

具体细节可以参考代码做进一步的思考。法一用init即可、这里的代码是法二init_dp()

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#define vi vector<int>
#define P pair<int,int>
using namespace std;
const int maxn = 1e6 + 10;
bool cond(int i)
{
    string line = to_string(i);
    if(line.find("62") != -1 || line.find("4") != -1)
        return true;
    return false;
}
bool lucky[maxn];
void init()
{
    for(int i = 1; i <= maxn; i++)
        if(!cond(i))
            lucky[i] = 1;
}
int f[10][10];//f[i][j]表示i位数中最高位为j的所有数中所满足条件的数的个数
void init_dp()
{
    for(int i = 0; i <= 9; i++)
        if(i != 4) f[1][i] = 1;
    for(int i = 2; i <= 7; i++)//数的位数
        for(int j = 0; j <= 9; j++)
        {
            if(j == 4) f[i][j] = 0;//如果最高位是4 显然位数为i最高位为4的满足条件的数为0
            else {
                for(int k = 0; k <= 9; k++)
                    f[i][j] += f[i - 1][k];
                if(j == 6) f[i][j] -= f[i - 1][2];//如果最高位是6 那么要减去次高位是2的满足条件的数的个数
            }
        }
}
int count(int t)
{
    int digit[10]={0};//记录每一位的数
    int num = 1;
    while(t)
    {
        digit[num++] = t % 10;
        t /= 10;
    }
    int ans = 0;
    for(int i = num - 1; i >= 1; i--)
    {
        for(int j = 0; j < digit[i]; j++)
        {
            if(j != 4 && !(digit[i+1] == 6 && j == 2))
            ans += f[i][j];
        }
        if(digit[i] == 4) break;
        if(digit[i] == 2 && digit[i + 1] == 6) break;
    }
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    init_dp();
    int n, m;
    while(cin >> n >> m && (n || m))
    {
        cout << count(m + 1) - count(n) << endl;
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值