HDU 2089

http://acm.hdu.edu.cn/showproblem.php?pid=2089

基础的数位dp,当初不懂数位dp水过去的,今天重新写一下,解释看注释

预处理+递推

学自http://wenku.baidu.com/view/9de41d51168884868662d623.html

#include <iostream>

using namespace std  ;

int f[8][10] ;//f[i][j]表示第i位是数j时符合条件的数字数量 

int digit[9] ;//digit[i]表示n从右到左第i位是多少 

void Init()

{

    f[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)//如果符合条件

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

            } 

        }

    }

}

int callen(int n)//计算n的长度 

{

    int cnt=0 ;

    while(n)

    {

        cnt++ ;

        n/=10 ;

    }

    return cnt ;

} 

void caldigit(int n,int len)//计算n的digit数组 

{

    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=callen(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)//第i位满足条件 

                ans+=f[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)) ;//用[0,m]-[0,n)即可得到区间[n,m] 

    }

    return 0 ;

}
View Code

记忆化搜索

学自http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.html
dfs里的e额外做下解释,当前位随意填的概念这么理解,比如一个数250,第一位填1,第二位填0-9都没问题,但是如果第一位填的是2,那么第二位只能填0-5

#include <iostream>

using namespace std ;

int f[8][2] ;//dp[i][0]:前i位符合要求 dp[i][1]:前i位符合要求且i+1位是6

int digit[9] ;//digit[i]表示n从右到左第i位是多少 

int dfs(int i,int s,bool e)//i表示当前位,s表示i位之前的状态,e表示当前位是否可以随意填写 

{ 

    if(i==0)

        return 1 ;

    if(!e && f[i][s]!=-1)

        return f[i][s] ;

    int res=0 ;

    int u=e?digit[i]:9 ;

    for(int d=0 ;d<=u ;d++)

    {

        if(d==4 || (s && d==2))

            continue ;

        res+=dfs(i-1,d==6,e&&d==u) ;

    }

    return e?res:f[i][s]=res ;

}

int callen(int n)//计算n的长度 

{

    int cnt=0 ;

    while(n)

    {

        cnt++ ;

        n/=10 ;

    }

    return cnt ;

}

void caldigit(int n,int len)//计算n的digit数组 

{

    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 len=callen(n) ;

    caldigit(n,len) ;

    dfs(len,0,1) ;

}

int main()

{

    int n,m ;

    memset(f,-1,sizeof(f)) ;

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

    {

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

            break ;

        printf("%d\n",solve(m)-solve(n-1)) ;//用[0,m]-[0,n)即可得到区间[n,m]

    }

    return 0 ;

}
View Code

 

你可能感兴趣的:(HDU)