hdu 5179 beautiful number【数位dp】

来源:互联网 发布:安卓和python 编辑:程序博客网 时间:2024/05/20 13:14

beautiful number

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 503    Accepted Submission(s): 304


Problem Description
Let A=ni=1ai10ni(1ai9)(n is the number of A's digits). We call A as “beautiful number” if and only if a[i]a[i+1] when 1i<n and a[i] mod a[j]=0 when 1in,i<jn(Such as 931 is a "beautiful number" while 87 isn't).
Could you tell me the number of “beautiful number” in the interval [L,R](including L and R)?
 


Input
The fist line contains a single integer T(about 100), indicating the number of cases.
Each test case begins with two integers L,R(1LR109).
 


Output
For each case, output an integer means the number of “beautiful number”.
 


Sample Input
21 11999999993 999999999
 


Sample Output
102
 

题意:一个递减数,并且要求上一位对下一位求余==0的一个数,就是一个beautiful number、问区间内有多少这种数、

暴力枚举一定会超时的、初次做数位dp的小伙伴们可以去切hdu 2089:http://blog.csdn.net/mengxiang000000/article/details/50614873

这里我们采用预处理+递推的方法来做,

首先我们要先预处理:

    //只有0%0==0 0特殊处理、    for(int i=1;i<10;i++)dp[1][i]=1;//1~9是全部符合条件的    for(int i=2;i<=10;i++)    {        for(int j=0;j<10;j++)//枚举i        {            for(int k=1;k<10;k++)//枚举i-1(下一位)            {                if(j%k==0)                dp[i][j]+=dp[i-1][k];            }            if(j==0)            dp[i][j]+=dp[i-1][0];        }    }

然后要对数据求出其长度和各个位上的数据:

 

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--)//从最高位开始枚举     {         if(i==len)//这里举个栗子:假如进来的数据是931、我们这里可以直接无脑加上1~900满足条件的数据、因为第一位上没有限制条件能够限制的住、没有可去掉的情况、         {             for(int j=0;j<digit[i];j++)             {                 ans+=dp[i][j];             }         }         else         {             if(digit[i]==0)break;//如果当前数变成了0、那么就不用向下枚举了、因为这里已经不可能再有后边的数小于0了             for(int j=1;j<=digit[i+1]&&j<digit[i];j++)//这里要让j同时满足两个条件、毕竟要求的是递减的数、             {                 if(digit[i+1]%j==0)//如果满足条件                 ans+=dp[i][j];//加上数据             }             if(digit[i+1]%digit[i]!=0)break;//如果当前数据的上一位对这位求余不等于0了、那么就不用考虑之后的内容了、因为这里已经不满足条件了、         }     }     return ans;}

细节处理理解了之后,这里直接上完整的AC代码:

#include<stdio.h>#include<string.h>using namespace std;int dp[10][10];int digit[10];void init(){    //只有0%0==0 0特殊处理、    for(int i=1;i<10;i++)dp[1][i]=1;//1~9是全部符合条件的    for(int i=2;i<=10;i++)    {        for(int j=0;j<10;j++)//枚举i        {            for(int k=1;k<10;k++)//枚举i-1(下一位)            {                if(j%k==0)                dp[i][j]+=dp[i-1][k];            }            if(j==0)            dp[i][j]+=dp[i-1][0];        }    }}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--)//从最高位开始枚举     {         if(i==len)         {             for(int j=0;j<digit[i];j++)             {                 ans+=dp[i][j];             }         }         else         {             if(digit[i]==0)break;             for(int j=1;j<=digit[i+1]&&j<digit[i];j++)             {                 if(digit[i+1]%j==0)                 ans+=dp[i][j];             }             if(digit[i+1]%digit[i]!=0)break;         }     }     return ans;}int main(){    init();    int t;    scanf("%d",&t);    while(t--)    {        int n,m;        scanf("%d%d",&n,&m);        printf("%d\n",solve(m+1)-solve(n));    }}

 




0 0
原创粉丝点击