数位DP总结

a positive integer number is beautiful if and only if it is divisible by each of its nonzero digits.
    问一个区间内[l,r]有多少个Beautiful数字
    范围9*10^18

思路一般,但是取模以及映射也是非常玄妙的

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define ll long long
//dp[i][j][k][sta] means the first i nomber,whether it has limit,the val%2520,the lcm of ezch number
ll pow[22],dp[22][2][2520][50];
int num[22],flcm[512],Map[2520],Map1[50],tmp[22],tot;
int gcd(int a,int b)
{
    if(!b) return a;
    return gcd(b,a%b);
}
int lcm(int a,int b)
{
    if(a==0) return b;
    if(b==0) return a;
    return a*b/gcd(a,b);
 } 
void init()
{
    pow[0]=1;
    for(int i=1;i<=18;i++)
    {
        pow[i]=pow[i-1]*10%2520;
    }
    for(int i=1;i<(1<<9);i++)
        for(int j=1;j<=9;j++)
            if(i&(1<<j-1)) flcm[i]=lcm(flcm[i-(1<<j-1)],j);
    for(int i=1;i<=(1<<9);i++)
        if(!Map[flcm[i]]) Map1[++tot]=flcm[i],Map[flcm[i]]=tot;    
}
ll solve(ll x)
{
    int len=0;
    ll ans=0;
    while(x) tmp[++len]=x%10,x/=10;
    for(int i=1;i<=len;i++)
    num[i]=tmp[len-i+1]; 
    memset(dp,0,sizeof(dp));
    dp[0][1][0][1]=1;
    for(int i=0;i<=len;i++)
        for(int j=0;j<2;j++)
            for(int k=0;k<2520;k++)
                for(int l=1;l<=tot;l++)
                {
                    ll p=dp[i][j][k][l];
                    int pp=Map1[l];
                    if(!p) continue;
                    if (i==len) 
                    {
                        if (k % pp==0) ans+=p;
                        continue;
                    }
                    if(!j)
                    {
                        for(int q=0;q<=9;q++)
                        {
                            dp[i+1][0][(k+q*pow[len-i-1])%2520][Map[lcm(pp,q)]]+=p;
                        }
                    }else
                    {
                        for(int q=0;q<num[i+1];q++)
                        {
                            dp[i+1][0][(k+q*pow[len-i-1])%2520][Map[lcm(pp,q)]]+=p;
                        }
                        dp[i+1][1][(k+num[i+1]*pow[len-i-1])%2520][Map[lcm(pp,num[i+1])]]+=p;
                    }
                }
    return ans;            
}
int main()
{
    init();
    int cas;
    scanf("%d",&cas);
    while(cas--)
    {
        ll a,b;
        scanf("%lld %lld",&a,&b); 
        printf("%lld
",solve(b)-solve(a-1));
    }
 } 
杭州人称那些傻乎乎粘嗒嗒的人为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
水体不解释
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int dp[25][2][2],tmp[25],num[25];
int solve(int x)
{
    int len=0,ans=0;
    while(x) tmp[++len]=x%10,x/=10;
    for(int i=1;i<=len;i++)
        num[len-i+1]=tmp[i];    
    memset(dp,0,sizeof(dp));    
    dp[0][1][0]=1;
    for(int i=0;i<=len;i++)
        for(int j=0;j<2;j++)
            for(int k=0;k<2;k++)
            {
                int pp=dp[i][j][k];
                if(!pp) continue;
                if(i==len)
                    ans+=pp;
                if(!j)
                {
                    if(!k)
                    {
                        for(int q=0;q<=9;q++)
                        if(q!=6&&q!=4) dp[i+1][0][0]+=pp;else if(q!=4)dp[i+1][0][1]+=pp;
                    }else
                    for(int q=0;q<=9;q++)
                    if(q!=6&&q!=2&&q!=4) dp[i+1][0][0]+=pp;else if(q==6) dp[i+1][0][1]+=pp; 
                }else
                {
                    if(!k)
                    {
                        for(int q=0;q<num[i+1];q++)
                        if(q!=6&&q!=4) dp[i+1][0][0]+=pp;else if(q!=4)dp[i+1][0][1]+=pp;
                        
                        if(num[i+1]!=6&&num[i+1]!=4) dp[i+1][1][0]+=pp;else if(num[i+1]!=4)dp[i+1][1][1]+=pp;
                    }else
                    {
                        for(int q=0;q<num[i+1];q++)
                        if(q!=6&&q!=2&&q!=4) dp[i+1][0][0]+=pp;else if(q==6) dp[i+1][0][1]+=pp;
                        if(num[i+1]!=6&&num[i+1]!=2&&num[i+1]!=4) dp[i+1][1][0]+=pp;else if(num[i+1]==6) dp[i+1][1][1]+=pp;
                    }
                }
            }
    return ans;             
}
int main()
{
    int a,b;
    while(~scanf("%d %d",&a,&b)&&(a||b))
    {
        printf("%d
",solve(b)-solve(a-1));
    }
 } 

 还有几道题

C - Bomb

 Solution:同上

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
ll tmp[55],num[55],len,dp[55][2][2];
ll solve(ll x)
{
    len=0;
    while(x)
    {
        tmp[++len]=x%10;
        x/=10;
    }
    memset(dp,0,sizeof(dp));
    for(ll i=1;i<=len;i++)
    num[len-i+1]=tmp[i];
    dp[0][1][0]=1;
    ll ans=0;
    for(ll i=0;i<=len;i++)
    {
        for(ll j=0;j<2;j++)
        {
            for(ll k=0;k<2;k++)
            {
                ll pp=dp[i][j][k];
                if(i==len)
                {
                    ans+=pp;
                    continue;
                }
                if(!pp) continue;
                if(!j)
                {
                    if(!k)
                    {
                        for(ll q=0;q<=9;q++)
                        if(q!=4)
                        dp[i+1][0][0]+=pp;else dp[i+1][0][1]+=pp;
                    }else
                    for(ll q=0;q<=8;q++)
                    {
                        if(q==4) dp[i+1][0][1]+=pp;else dp[i+1][0][0]+=pp;
                    }
                }else
                {
                    if(!k)
                    {
                        for(ll q=0;q<num[i+1];q++)
                        {
                            if(q==4)
                            {
                                dp[i+1][0][1]+=pp;
                            }else dp[i+1][0][0]+=pp;
                        }
                        if(num[i+1]==4) dp[i+1][1][1]+=pp;else dp[i+1][1][0]+=pp;
                    }else
                    {
                        for(ll q=0;q<num[i+1];q++)
                        if(q!=9)
                        {
                            if(q==4)
                            {
                                dp[i+1][0][1]+=pp;    
                            }else dp[i+1][0][0]+=pp;
                        }
                        if(num[i+1]!=9)
                        {
                            if(num[i+1]==4)
                            {
                                dp[i+1][1][1]+=pp;
                            }else dp[i+1][1][0]+=pp;
                        }
                     } 
                }
            }
        }
    }
    return ans;
}
int main()
{
    ll cas,n;
    scanf("%lld",&cas);
    while(cas--)
    {
        scanf("%lld",&n);
        printf("%lld
",n-solve(n)+1);
    }
}

D - Round Numbers

 第一个用dfs写的题
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
int dp[40][40][40],num[40];
ll dfs(ll pos,ll num0,ll num1,bool flag,bool z)
{
    if(pos==-1) return num0>=num1;
    if(!flag&&dp[pos][num0][num1]!=-1) return dp[pos][num0][num1];
    ll maxn=flag?num[pos]:1;
    ll ans=0;
    for(ll i=0;i<=maxn;i++)
        ans+=dfs(pos-1,(z&&i==0)?0:num0+(i==0),(z&&i==0)?0:num1+(i==1),flag&&i==maxn,z&&i==0);
    if(!flag) dp[pos][num0][num1]=ans;
    return ans;
}
ll cal(ll x)
{
    ll pos=0;
    while(x)
    {
        num[pos++]=x%2;
        x/=2;
    }
    return dfs(pos-1,0,0,1,1);
}
int main()
{
    ll n,m;
    memset(dp,-1,sizeof(dp));
   scanf("%lld%lld",&n,&m);
        printf("%lld
",cal(m)-cal(n-1));
    return 0;
}

E - Balanced Number

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
ll l,r;
ll tmp[25],num[25],f[25][25][2005];
ll dfs(ll pos,ll x,ll sum,ll limit)
{
    if(!pos) return sum==0;
    if(sum<0) return 0;
    ll ans=0;
    if(!limit&&~f[pos][x][sum]) return f[pos][x][sum];
    ll up=limit? num[pos]:9;
    for(ll i=0;i<=up;i++)
    {
        ans+=dfs(pos-1,x,sum+(i)*(pos-x),limit&&(i==up));
    }
    if(!limit) f[pos][x][sum]=ans;
    return ans;
}
ll solve(ll x)
{
    ll len=0;
    while(x)
    {
        tmp[++len]=x%10;
        x/=10;
    }
    ll sum=0;
    for(ll i=1;i<=len;i++)    num[i]=tmp[i];
    for(ll i=1;i<=len;i++) sum+=dfs(len,i,0,1);
    return sum-len+1;
}
int main()
{
    ll T;
    memset(f,-1,sizeof(f));
    scanf("%lld",&T);
    while(T--)
    {
        scanf("%lld %lld",&l,&r);
        printf("%lld
",solve(r)-solve(l-1));
     } 
 } 

bzoj1388

一不小心先打成爆艘了,后来改成记忆化

F - B-number

 

 好题啊,其实就是上面两题的组合

有个容斥的思想

可以被13整除的数且含13的数的数量等于 (所有的数的数量-不是13倍数的数)-是13倍数又不能含13的数

括号中的答案可以化简为n/13

剩下的就是数位Dp的模板了

Niro BC说记忆化垃圾,但是有些情况Dp真的麻烦,就两个条件就长成这鸟样%%

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int tmp[50],num[50],dp[50][2][2][13],ans,n;
int solve(int n)
{
    memset(dp,0,sizeof(dp));
    int len=0;
    while(n)
    {
        tmp[++len]=n%10;
        n/=10;
    }
    int ans=0;
    for(int i=1;i<=len;i++) num[len-i+1]=tmp[i];
    dp[0][1][0][0]=1;
    for(int i=0;i<=len;i++)
        for(int j=0;j<2;j++)
            for(int k=0;k<2;k++)
                for(int sta=0;sta<13;sta++)
                {
                    int pp=dp[i][j][k][sta];
                    if(!pp) continue;
                    if(i==len) if(sta==0) ans+=pp;
                    if(!j)
                    {
                        if(!k)
                        {
                            for(int q=0;q<=9;q++)
                            if(q!=1)
                            dp[i+1][0][0][(sta*10+q)%13]+=pp;else dp[i+1][0][1][(sta*10+q)%13]+=pp;
                        }else
                        for(int q=0;q<=9;q++)
                        if(q!=3)
                        {
                            if(q!=1)
                            dp[i+1][0][0][(sta*10+q)%13]+=pp;else dp[i+1][0][1][(sta*10+q)%13]+=pp;
                        }
                    }else
                    {
                        if(!k)
                        {
                            for(int q=0;q<num[i+1];q++)
                            if(q!=1) dp[i+1][0][0][(sta*10+q)%13]+=pp;else dp[i+1][0][1][(sta*10+q)%13]+=pp;
                            if(num[i+1]!=1) dp[i+1][1][0][(sta*10+num[i+1])%13]+=pp;else dp[i+1][1][1][(sta*10+num[i+1])%13]+=pp; 
                        }else
                        {
                            for(int q=0;q<num[i+1];q++)
                            if(q!=3)
                            {
                                if(q!=1)
                                dp[i+1][0][0][(sta*10+q)%13]+=pp;else dp[i+1][0][1][(sta*10+q)%13]+=pp;
                            }
                            if(num[i+1]!=3)
                            {
                                if(num[i+1]!=1) dp[i+1][1][0][(sta*10+num[i+1])%13]+=pp;else dp[i+1][1][1][(sta*10+num[i+1])%13]+=pp;
                            }
                        }
                    }
                } 
        return ans;        
}
int main()
{
    while(~scanf("%d",&n))
    {
        printf("%d
",n/13-solve(n)+1);
    }
 } 
原文地址:https://www.cnblogs.com/dancer16/p/7719808.html