您的位置:首页 > 其它

HDU 2089 不要62(数位DP)

2017-11-14 17:52 429 查看
不要62
Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u
Submit Status

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


0;j#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <cmath>

using namespace std;

const int N = 11;

int dp

, dit
;

int solve(int x)

{

    memset(dit,0,sizeof(dit));

    int k=0;

    while(x)

    {

        dit[++k]=x%10;

        x/=10;

    }

    int ans=0;

    for(int i=k;i;i--)

    {
        for(int j=<dit[i];j++)

        {

            if(j==4||(j==2&&dit[i+1]==6))

                continue;

            ans+=dp[i][j];

        }

        if(dit[i]==4||(dit[i]==2&&dit[i+1]==6))

            break;

    }

    return ans;

}

int main()

{

    memset(dp,0,sizeof(dp));

    dp[0][0]=1;

    for(int i=1;i<10;i++)

    {

        for(int j=0;j<10;j++)

        {

            if(j==4)

                continue;

            for(int k=0;k<10;k++)

            {

                if(j==6&&k==2)

                    continue;

                dp[i][j]+=dp[i-1][k];

            }

        }

    }

    int l, r;

    while(scanf("%d %d", &l, &r),l||r)

    {

        printf("%d\n",solve(r)-solve(l));//相当于[0,r)-[0,l);

    }

    return 0;

}

借鉴:

我们首先要对dp【】【】数组进行确定含义和预处理、这里我们规定dp【i】【j】表示的是以j开头的i位数的符合条件的个数、我们这里说的可能不是很容易理解,我们这里举个栗子:dp【2】【6】=8、表示从60~69中满足条件的个数、60、61、63、65、66、67、68、69(8个~)

我们这里再举个栗子dp【3】【0】:表示从1~100中满足条件的个数、(这里就不枚举了、、、)【0,99】

那么dp【3】【1】呢?表示从100~200中满足条件的个数、【100,199】

这里我们知道了dp数组的含义,那么我们就来进行预处理吧:这里我们顺便贴上dp数组内数据,供参考

1 1 1 1 0 1 1 1 1

9 9 9 9 0 9 8 9 9

80 80 80 80 0 80 71 80 80

711 711 711 711 0 711 631 711 711

6319 6319 6319 6319 0 6319 5608 6319 6319

56160 56160 56160 56160 0 56160 49841 56160 56160

499121 499121 499121 499121 0 499121 442961 499121 499121

初始化dp代码://对于这部分代码、仔细看、认真看、领悟领悟就懂了~

[cpp] view
plain copy

 print?





dp[0][0]=1;<span style="white-space:pre">                                                                     </span>      //十位dp加个位dp、百位dp加十位dp(从而就加上了个位dp)<span style="white-space:pre">    </span>  

for(int i=1;i<=7;i++)  

{  

    for(int j=0;j<10;j++)//枚举第i位数上的数字、  

    {  

        for(int k=0;k<10;k++)//枚举第i-1位上的数字、  

        {  

            if(!(j==6&&k==2)&&j!=4)//满足条件  

            dp[i][j]+=dp[i-1][k];  

        }  

    }  

}  

这里我们知道了整块整块的数据之后呢(比如dp【2】【6】我们知道了是什么含义,但是我们要对整块整块的数据加和才能得到最终结果)就要对小细节进行处理了~有了以上的数据之后呢,我们知道,处理当前这个数之前,需要两个元素:

1、各个位上的数据

2、数据的长度

这里我们很容易就能用函数来实现:

[cpp] view
plain copy

 print?





int calchangdu(int n)//长度  

{  

    int cont=0;  

    while(n)  

    {  

        cont++;  

        n/=10;  

    }  

    return cont;  

}  

int caldigit(int n,int len)//各个位上的数据  

{  

    memset(digit,0,sizeof(digit));  

    for(int i=1;i<=len;i++)  

    {  

        digit[i]=n%10;  

        n/=10;  

    }  

}  

有了这么些个已知条件之后、我们只要对数据逐一判断处理就行了~

我们这里求得【0,n)的满足条件的个数的方法如下:

[cpp] view
plain copy

 print?





int solve(int n)//计算[0,n)符合条件的个数//※这个函数是最主干的部分  

{  

     int ans=0;  

     int len=calchangdu(n);  

     caldigit(n,len);  

     for(int i=len;i>=1;i--)//从最高位开始枚举  

     {  

         for(int j=0;j<digit[i];j++)//枚举第i位包含的数据  

         {  

             if(!(j==2&&digit[i+1]==6)&&j!=4)//当然要满足条件才能加、  

             {  

                 ans+=dp[i][j];  

             }  

         }  

        if(digit[i]==4 || (digit[i]==2 && digit[i+1]==6))//第i位已经不满足条件,则i位以后都不可能满足条件,结束循环  

            break ;  

     }  

     return ans;  

}  

所有内容都确定好了之后,那么求【n,m】的方法也很直接了:

求【0,m】的个数,然后求【0,n-1】的个数,然后相减,就得到了最终得数。我们这里上完整的AC代码:

[cpp] view
plain copy

 print?





#include<stdio.h>  

#include<string.h>  

using namespace std;  

int dp[10][10];  

int digit[10];  

void init()  

{  

    //十位加个位dp,百位加十位dp,千位加百位dp  

    dp[0][0]=1;  

    for(int i=1;i<=7;i++)  

    {  

        for(int j=0;j<10;j++)//枚举第i位数上的数字、  

        {  

            for(int k=0;k<10;k++)//枚举第i-1位上的数字、  

            {  

                if(!(j==6&&k==2)&&j!=4)//满足条件  

                dp[i][j]+=dp[i-1][k];  

            }  

        }  

    }  

}  

int calchangdu(int n)  

{  

    int cont=0;  

    while(n)  

    {  

        cont++;  

        n/=10;  

    }  

    return cont;  

}  

int caldigit(int n,int len)  

{  

    memset(digit,0,sizeof(digit));  

    for(int i=1;i<=len;i++)  

    {  

        digit[i]=n%10;  

        n/=10;  

    }  

}  

int solve(int n)//计算[0,n)符合条件的个数  

{  

     int ans=0;  

     int len=calchangdu(n);  

     caldigit(n,len);  

     for(int i=len;i>=1;i--)//从最高位开始枚举  

     {  

         for(int j=0;j<digit[i];j++)  

         {  

             if(!(j==2&&digit[i+1]==6)&&j!=4)  

             {  

                 ans+=dp[i][j];  

             }  

         }  

        if(digit[i]==4 || (digit[i]==2 && digit[i+1]==6))//第i位已经不满足条件,则i位以后都不可能满足条件,结束循环  

            break ;  

     }  

     return ans;  

}  

int main()  

{  

    init();  

    int n,m;  

    while(~scanf("%d%d",&n,&m))  

    {  

        if(n==0&&m==0)break;  

        printf("%d\n",solve(m+1)-solve(n));  

    }  

}  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: