hdu 5179-beautiful number

題目:給定一個區間的兩個區間端點l和r,問在l和r之間有多少滿足下列情況的數字---這個數字應該滿足前一位是後一位的倍數。

分析:這是一個經典的數位DP的題目,我們可以把區間求和轉化成兩個區間的減法,即S[1,r]-S[1,l-1]就是我們求得的答案。

         那麼問題就轉化成了求1-n之間滿足情況的數的個數,那麼我們就要使用dp[i][j]求得數字長度爲i,首數字爲j的滿足情況的數的個數,然後累加進行預處理(具體見代碼)。

         隨後分爲以下幾種情況討論:1.比數字n位數少的,直接累加sum[i](爲長度爲i的所有滿足情況的數的和,即dp[i][j](j>=1&&j<=9)的和)就可以了。

     2.與數字n位數相同,但首數字比n的首數字小的,直接加上dp[n的長度][j](j>=1&&j<n的首字母)

    3.與數字n位數相同,且首數字也相同的,使用dfs求解即可。


#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;

int Sum[10],dp[10][10];

void init()
{
    memset(Sum,0,sizeof(Sum));
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=9;i++)
    {
        dp[1][i]=1;
    }
    for(int i=2;i<=9;i++)
    {
        for(int j=1;j<=9;j++)
        for(int k=1;k<=j;k++)
        if(j%k==0)
        {
            dp[i][j]+=dp[i-1][k];
        }
    }
    for(int i=1;i<=9;i++)
    {
        for(int j=1;j<=9;j++)
        Sum[i]+=dp[i][j];
        //printf("%d\n",Sum[i]);
    }
}

int dfs(int nm,int st,int len,int dit[])
{
    if(nm>dit[st]) return 0;
    if(nm<dit[st]) return dp[len-st][nm];
    if(nm==dit[st])
    {
        if(st==len-1) return 1;
        int ans=0;
        for(int i=nm;i>=1;i--)
        if(nm%i==0)
        ans+=dfs(i,st+1,len,dit);
        return ans;
    }
}

int Cal(int n)
{
    if(n<=9) return n;
    int dit[10];
    int len=log10(n)+1;
    //cout<<n<<":"<<len<<endl;
    int ans=0;
    for(int i=1;i<len;i++)
    {
        ans+=Sum[i];
    }
    //cout<<"位數少的ans:"<<ans<<endl;
    for(int i=len-1;i>=0;i--)
    {
        dit[i]=n%10;
        n/=10;
    }
    //cout<<"dit0:"<<dit[0]<<endl;
    for(int i=1;i<dit[0];i++)
    {
        ans+=dp[len][i];
    }
    //cout<<"同位數小於dit0的ans:"<<ans<<endl;
    for(int i=dit[0];i>=1;i--)
    if(dit[0]%i==0)
    ans+=dfs(i,1,len,dit);
    //cout<<"Sum-ans:"<<ans<<endl;
    return ans;
}

int main()
{
    init();
    //int dit[10];
    int l=0,r=0,num;
    /*for(int i=1;i<=20;i++)
    {
        cout<<i<<":";
        num=i;
        l=Cal(num);
        int len=log10(num);
        for(int j=len;j>=0;j--)
        {
            dit[j]=num%10;
            num/=10;
        }
        int flag=1;
        for(int j=0;j<len;j++)
        if(dit[j+1]==0||dit[j]%dit[j+1])
        {
            flag=0;break;
        }
        r+=flag;
        printf("%d %d\n",l,r);
        if(l!=r)
        {
            printf("ERROR!\n");
            break;
        }
    }*/
    scanf("%d",&num);
    while(num--)
    {
        scanf("%d%d",&l,&r);
        if(l>=1e9) l--;
        if(r>=1e9) r--;
        l--;
        l=Cal(l);
        r=Cal(r);
        //cout<<l<<" "<<r<<endl;
        printf("%d\n",r-l);
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章