HDU 数位dp

模板http://www.cnblogs.com/jffifa/archive/2012/08/17/2644847.html

完全理解以后,我发现这种写法实在是太厉害了,简洁,优美,可以回避很多细节问题,而这些细节如果用递推的方法写,处理起来可能会非常痛苦

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

不要62

http://www.cnblogs.com/xiaohongmao/p/3473599.html

前几天写过这道题的解题报告,两种解法都有

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

不要49

#include <iostream>

using namespace std ;

typedef __int64 ll ;

ll dp[25][2] ;

int digit[25] ;

ll dfs(int i,int s,bool e)

{

    if(!i)return 1 ;

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

        return dp[i][s] ;

    ll res=0 ;

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

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

    {

        if(s && d==9)

            continue ;

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

    }

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

}

int callen(ll n)

{

    int cnt=0 ;

    while(n)

    {

        cnt++ ;

        n/=10 ;

    }

    return cnt ;

}

void caldigit(ll n,int len)

{

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

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

    {

        digit[i]=n%10 ;

        n/=10 ;

    }

}

ll solve(ll n)

{

    int len=callen(n) ;

    caldigit(n,len) ;

    return dfs(len,0,1) ;

}

int main()

{ 

    int t ;

    scanf("%d",&t) ;

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

    while(t--)

    {

        ll n ;

        scanf("%I64d",&n) ;

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

    }

    return 0 ;

}
View Code

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

出现13且是13的倍数

#include <iostream>

using namespace std ;

int dp[15][15][2][15] ;//当前位数 被13除的余数 是否包括13 最末一位数 

int digit[15] ;

int dfs(int i,int re,bool flag,int last,bool e)

{

    if(!i)return !re && flag ;

    if(!e && dp[i][re][flag][last]!=-1)

        return dp[i][re][flag][last] ;

    int res=0 ;

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

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

        res+=dfs(i-1,(re*10+d)%13,flag || (last==1 && d==3),d,e && d==u) ;

    return e?res:dp[i][re][flag][last]=res ;

}

int callen(int n)

{

    int cnt=0 ;

    while(n)

    {

        cnt++ ;

        n/=10 ;

    }

    return cnt ;

}

void caldigit(int n,int len)

{

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

    {

        digit[i]=n%10 ;

        n/=10 ;

    }

}

int solve(int n)

{

    int len=callen(n) ;

    caldigit(n,len) ;

    return dfs(len,0,0,0,1) ;

}

int main()

{

    int n ;

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

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

    {

        printf("%d\n",solve(n)) ;

    }

    return 0 ;

}
View Code

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

和上一题很像,要求x整除f(x),由于f(x)范围较小,枚举f(x)就好,注意的地方是有点卡内存,数组要开的比较合适才能过,习惯性开大点会MLE

#include <iostream>

#include <cstdio>

#include <cstring>

using namespace std ;

int digit[11] ;

int dp[11][84][84][84] ;

int w ;

int dfs(int i,int s,int mod,int e)

{

    if(!i)return !mod && s==w ;

    if(!e && dp[i][s][mod][w]!=-1)return dp[i][s][mod][w] ;

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

    int res=0 ;

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

        res+=dfs(i-1,s+d,(mod*10+d)%w,e && d==u) ;

    return e?res:dp[i][s][mod][w]=res ;

}

int callen(int n)

{

    int cnt=0 ;

    while(n)

    {

        cnt++ ;

        n/=10 ;

    }

    return cnt ;

}

void 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)

{

    int len=callen(n) ;

    caldigit(n,len) ;

    int ans=0 ;

    for(w=1 ;w<84 ;w++)

        ans+=dfs(len,0,0,1) ;

    return ans ;

}

int main()

{

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

    int t ;

    scanf("%d",&t) ;

    for(int cas=1 ;cas<=t ;cas++)

    {

        int l,r ;

        scanf("%d%d",&l,&r) ;

        printf("Case %d: %d\n",cas,solve(r)-solve(l-1)) ;

    }

    return 0 ;

}
View Code

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

枚举平衡位置

#include <iostream>

#include <cstdio>

#include <cstring>

using namespace std ;

typedef __int64 ll ;

int digit[25] ;

ll dp[25][25][1500] ;

int w ;

ll dfs(int i,int s,int e)

{

    if(!i)return !s ;

    if(!e && dp[i][w][s]!=-1)return dp[i][w][s] ;

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

    ll res=0 ;

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

        res+=dfs(i-1,s+d*(i-w),e && d==u) ;

    return e?res:dp[i][w][s]=res ;

}

int callen(ll n)

{

    int cnt=0 ;

    while(n)

    {

        cnt++ ;

        n/=10 ;

    }

    return cnt ;

}

void caldigit(ll n,int len)

{

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

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

    {

        digit[i]=n%10 ;

        n/=10 ;

    }

}

ll solve(ll n)

{

    int len=callen(n) ;

    caldigit(n,len) ;

    ll ans=0 ;

    for(w=1 ;w<=len ;w++)

        ans+=dfs(len,0,1) ;

    return ans-len+1 ;

}

int main()

{

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

    int t ;

    scanf("%d",&t) ;

    while(t--)

    {

        ll l,r ;

        scanf("%I64d%I64d",&l,&r) ;

        printf("%I64d\n",solve(r)-solve(l-1)) ;

    }

    return 0 ;

}
View Code

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

和4389一样,不过除数只有10一个,更简单

#include <iostream>

#include <cstdio>

#include <cstring>

using namespace std ;

typedef __int64 ll ;

int digit[25] ;

ll dp[25][105][15] ;

ll dfs(int i,int s,int mod,int e)

{

    if(!i)return !mod ;

    if(!e && dp[i][s][mod]!=-1)return dp[i][s][mod] ;

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

    ll res=0 ;

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

        res+=dfs(i-1,s+d,(mod+d)%10,e && d==u) ;

    return e?res:dp[i][s][mod]=res ;

}

int callen(ll n)

{

    int cnt=0 ;

    while(n)

    {

        cnt++ ;

        n/=10 ;

    }

    return cnt ;

}

void caldigit(ll n,int len)

{

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

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

    {

        digit[i]=n%10 ;

        n/=10 ;

    }

}

ll solve(ll n)

{

    int len=callen(n) ;

    caldigit(n,len) ;

    return dfs(len,0,0,1) ;

}

int main()

{

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

    int t ;

    scanf("%d",&t) ;

    for(int cas=1 ;cas<=t ;cas++)

    {

        ll l,r ;

        scanf("%I64d%I64d",&l,&r) ;

        printf("Case #%d: %I64d\n",cas,solve(r)-solve(l-1)) ;

    }

    return 0 ;

}
View Code


 

你可能感兴趣的:(HDU)