HDU2089-不要62

不要62

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


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的入门题目。先附上ppt的下载链接,讲得很不错的。链接:点击打开链接

首先是初始化dp数组。dp数组保存的就是以  j  为开头的  i  位数字符合要求的数字有多少个。

void init()
{
    memset(dp,0,sizeof(dp));
    dp[0][0]=1;//看题目要求,一般状态下以0为开头的0位数状态为1
    for(int i=1;i<=7;i++)//一共最大是7位数
    {
        for(int j=0;j<=9;j++)//从右往左,先模拟第 i 位的取值
        {
            for(int k=0;k<=9;k++)//再摸拟第  i-1 位的取值
            {
                if(j!=4&&!(j==6&&k==2))//判断是否符合条件描述
                {
                    dp[i][j]+=dp[i-1][k];//如果满足条件相当于第 i 位(取值为j)共 i 位数的状态上加上第 i-1 位(取值为k)共 i-1 位的状态
                }
            }
        }
    }
}

然后计算出来digit数组的值,把数倒序保存在数组中

int cntlen(int n)
{
    int len=0;
    while(n)
    {
        len++;
        n/=10;
    }
    return len;
}
void cal(int n, int len)
{
    memset(digit,0,sizeof(digit));
    for(int i=1;i<=len;i++)
    {
        digit[i]=n%10;
        n/=10;
    }
}

最后是如何解决问题的函数

int slove(int n)//其实计算的是【0,n-1】的状态数,这一点PPT中讲到过。对于一个小于n的数,肯定是从高位到低位出现某一位<n的那一位。
{
int sum=0;//最终的状态数之和 int len=cntlen(n); cal(n,len); //这一部分很关键   for(int i=len;i>=1;i--)//因为数组是倒序保存的,相当于从原来数的个位开始,直到最高位 { for(int j=0;j<digit[i];j++)//这里的 j 模拟的是当前位置的取值,肯定不能等于digit[i]。 //因也是因为ppt中讲过的性质:对于一个小于n的数,肯定是从高位到低位出现某一位<n的那一位。 { if(!(digit[i+1]==6&&j==2)&&j!=4) { sum+=dp[i][j];//符合条件的累加到sum中 } } //下面这个if的位置千万不能放在前面,原因我在下面说   if(digit[i]==4||(digit[i+1]==6&&digit[i]==2))//当遇到当前位是4或是当前位是2并且下一位是6,就没有必要再进行下去。 //因为往下走,前面遍历过的位置是不会变的,已经出现了62或者4,后面无论怎么变化都是不合规定的。 { break; } } return sum; }

第一:为什么上面代码中的 j 取值必须小于digit[i]?

因为开始表述的性质,我们知道一段区间内符合要求的数是不会等于它本身的。

举例:给出一个数字:123624

先是最高位1的取值,它可以取0。然后开始计算他的状态。他的最大值是0,然后假设后面位置都最大可以取9,得到的数字就是099999。这个数字是小于100000的。

然后跳到下一位2的时候,最高位固定为1不变,就不再需要考虑。接着再考虑下一位。

实际上可以把数字进行拆分:123624=100000+20000+3000+600+20+4

100000之内计算的是【100000,199999】区间的状态

20000之内计算的是  【10000,19999】区间的状态

3000之内计算的是    【1000,2999】区间的状态

600之内计算的是      【100,599】区间的状态

20之内计算的是        【10,19】区间的状态

4之内计算的是          【0,3】区间的状态

前面每一位少的都会在下一位补充进去,但是最后4没有进行补充。合并整个区间就得到的是【0,123623】的状态。


第二:为什么if必须放在后面不放在前面?

因为 j 的取值是小于当前位digit[i]的。

还是上面的例子,123624,倒序放置在数组中就成为了426321。

依照程序从最低位4开始,取值0~3。这个是合法的。但如果把if放在前面,这部分的状态就没有加上去,最终结果就会变小。

当遇到当前位是4或是当前位是2并且下一位是6,就没有必要再进行下去。因为往下走,前面遍历过的位置是不会变的,后面无论怎么变化都是不合规定的。


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
using namespace std;
int dp[8][10];
int digit[10];
void init()
{
    memset(dp,0,sizeof(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];
                }
            }
        }
    }
}
int cntlen(int n)
{
    int len=0;
    while(n)
    {
        len++;
        n/=10;
    }
    return len;
}
void cal(int n, int len)
{
    memset(digit,0,sizeof(digit));
    for(int i=1;i<=len;i++)
    {
        digit[i]=n%10;
        n/=10;
    }
}
int slove(int n)
{
    int sum=0;
    int len=cntlen(n);
    cal(n,len);
    for(int i=len;i>=1;i--)
    {
        for(int j=0;j<digit[i];j++)
        {
            if(!(digit[i+1]==6&&j==2)&&j!=4)
            {
                sum+=dp[i][j];
            }
        }
        if(digit[i]==4||(digit[i+1]==6&&digit[i]==2))
        {
            break;
        }
    }
    return sum;
}
int main()
{
    int n,m;
    init();
    while(~scanf("%d%d",&n,&m))
    {
        if((n==0)&&(m==0))
        {
            break;
        }
        printf("%d\n",slove(m)-slove(n-1));
    }
    return 0;
}


posted @ 2017-11-03 01:43  lemonsbiscuit  阅读(112)  评论(0编辑  收藏  举报