数位dp:

今天突然做到一个数位dp的题目,才发现一直没有数位dp的板子。
在找数位dp的题目的时候,发现我的树形dp大概也需要整理一下了?

一、不要62 HDU - 2089
题意:
某个区间中,数字中不含有4或者62数有多少个。

题解:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
using namespace std;
int dp[20][2];int x[20];
int dfs(int pos,int pre,int sta,bool limit)
{
    int sum=0;
    if(pos==-1) return 1;
    if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];
    int up=(limit?x[pos]:9);
    for(int i=0;i<=up;i++)
    {
        if(pre==6&&i==2) continue;
        if(i==4) continue;
        sum+=dfs(pos-1,i,i==6,limit&&i==x[pos]);
    }
    if(!limit) dp[pos][sta]=sum;
    return sum;
}

int f(int t)
{
    int pos=0;
    while(t)
    {
        x[pos++]=t%10;
        t/=10;
    }
    return dfs(pos-1,-1,0,true);
}

int main(void)
{
    int n,m;
    memset(dp,-1,sizeof(dp));
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0) break;
        int ans=f(m)-f(n-1);
        cout<<ans<<endl;
    }
    return 0;
}

二、F(x) HDU - 4734
题意:对于一个有n位(这n位从高位到低位分别是An An-1 An-2 … A2 A1 )的十进制数,我们定义它的权值F(x)=An * 2n-1 + An-1 * 2n-2 + … + A2 * 2 + A1 * 1.现在给你两个数A,B,请计算[0,B]范围内有多少个权值<=F(A)的数

题解:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define llu unsigned ll
using namespace std;
int dp[20][11000];
int x[20];
int n,m;

int F(int x)
{
    int p=1;
    int ans=0;
    while(x)
    {
        ans+=p*(x%10);
        x/=10;
        p<<=1;
    }
    return ans;
}


int dfs(int pos,int sta,bool limit)
{
    if(sta<0) return 0;
    if(pos<0) return sta>=0;

    if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];

    int up=limit?x[pos]:9;

    int ans=0;
    for(int i=0;i<=up;i++)
    {
        ans+=dfs(pos-1,sta-i*(1<<pos),limit&&i==up);
    }

    if(!limit) dp[pos][sta]=ans;
    return ans;
}



int f(int t)
{
    int pos=0;
    while(t)
    {
        x[pos++]=t%10;
        t/=10;
    }
    return dfs(pos-1,F(n),true);
}


int main(void)
{
    int t;
    int pp=0;
    memset(dp,-1,sizeof(dp));
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        printf("Case #%d: %d\n",++pp,f(m));
    }
}

三、 B-number HDU - 3652

题意:0–n中含有数字13且能被13整除的数有多少个。

题解:

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<vector>
#include<algorithm>
#define ll long long
#define llu unsigned ll
using namespace std;
int dp[20][2][2][15];
int x[20];

int dfs(int pos,int last,bool pre,bool sta,bool limit)
{
    int sum=0;
    if(pos==-1)
    {
        if(sta&&last==0) return 1;
        else return 0;
    }

    if(!limit&&dp[pos][pre][sta][last]!=-1) return dp[pos][pre][sta][last];

    int up=limit?x[pos]:9;

    for(int i=0;i<=up;i++)
        sum+=dfs(pos-1,(last*10+i)%13,i==1,sta||pre&&i==3,limit&&i==up);

    if(!limit) dp[pos][pre][sta][last]=sum;
    return sum;
}

int f(int t)
{
    int pos=0;
    while(t)
    {
        x[pos++]=t%10;
        t/=10;
    }
    return dfs(pos-1,0,false,false,true);
}

int main(void)
{
    int n;
    memset(dp,-1,sizeof(dp));
    while(scanf("%d",&n)!=EOF)
    {
        printf("%d\n",f(n));
    }
    return 0;
}

四、Balanced Number HDU - 3709

题意:
一个数字是平衡数字,当且仅当存在它的一个数位作为平衡点,这个点左边和右边【数字*到这个点的距离】之和相等

题解:

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<vector>
#include<algorithm>
#define ll long long
#define llu unsigned ll
using namespace std;
int x[20];
ll dp[20][20][4000];

ll dfs(int pos,int p,int num,bool limit)
{
    ll sum=0;
    if(pos==-1)
    {
        if(num==0) return 1;
        else return 0;
    }

    if(num<0) return 0;

    if(!limit&&dp[pos][p][num]!=-1) return dp[pos][p][num];

    int up=limit?x[pos]:9;

    for(int i=0;i<=up;i++)
        sum+=dfs(pos-1,p,num+(pos-p)*i,limit&&i==up);

    if(!limit) dp[pos][p][num]=sum;
    return sum;
}

ll f(ll t)
{
    int pos=0;
    if(t<0) return 0;
    while(t)
    {
        x[pos++]=t%10;
        t/=10;
    }
    ll sum=0;
    for(int i=0;i<pos;i++)
        sum+=dfs(pos-1,i,0,true);
    return sum-pos+1;
}

int main(void)
{
    int t;
    scanf("%d",&t);
    memset(dp,-1,sizeof(dp));
    while(t--)
    {
        ll x,y;
        scanf("%lld%lld",&x,&y);
        printf("%lld\n",f(y)-f(x-1));
    }
    return 0;
}

五、吉哥系列故事——恨7不成妻 HDU - 4507

题意:
如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
  1、整数中某一位是7;
  2、整数的每一位加起来的和是7的整数倍;
  3、这个整数是7的整数倍;

现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<vector>
#include<algorithm>
#define ll long long
#define llu unsigned ll
using namespace std;
const int mod=1e9+7;
int x[20];

ll dp[20][10][10];
ll cnt[20][10][10];
ll sum[20][10][10];
ll p[20];

ll dfs(int pos,int add,int div,bool limit,ll &ss,ll &dd,ll &cc)
{

    if(pos==-1)
    {
        dd=0,ss=0;
        if(add!=0&&div!=0) cc=1;
        else cc=0;
        return dd;
    }

    if(!limit&&dp[pos][add][div]!=-1)
    {
        ss=sum[pos][add][div];
        dd=dp[pos][add][div];
        cc=cnt[pos][add][div];
        return dp[pos][add][div];
    }

    int up=limit?x[pos]:9;

    ss=0,dd=0,cc=0;
    ll s,d,c;
    for(int i=0;i<=up;i++)
    {
        if(i==7) continue;
        dfs(pos-1,(add+i)%7,(div*10+i)%7,limit&&i==up,s,d,c);
        ll k=i*p[pos]%mod;
        cc=(cc+c)%mod;
        ss=(((c*k%mod)+s)%mod+ss)%mod;
        dd=((c*k%mod*k%mod)%mod+d%mod+2*k*s%mod+dd%mod)%mod;
    }

    if(!limit)
    {
        sum[pos][add][div]=ss;
        cnt[pos][add][div]=cc;
        dp[pos][add][div]=dd;
    }
    return dd;

}

ll f(ll t)
{
    int pos=0;
    while(t)
    {
        x[pos++]=t%10;
        t/=10;
    }
    ll s,d,c;
    return dfs(pos-1,0,0,true,s,d,c);

}

int main(void)
{
    int t;
    scanf("%d",&t);
    memset(dp,-1,sizeof(dp));
    p[0]=1;
    for(int i=1;i<20;i++)
        p[i]=p[i-1]*10%mod;
    while(t--)
    {
        ll x,y;
        scanf("%lld%lld",&x,&y);
        printf("%lld\n",((f(y)-f(x-1))%mod+mod)%mod);
    }
    return 0;
}

六、Balanced Numbers SPOJ - BALNUM

题意:
求区间内数字满足“奇数各数出现偶数次,偶数各数出现奇数次”的数字的个数

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#define ll long long
using namespace std;
int x[20];
ll dp[20][60005];
ll cm[11];

bool check(int sta)
{
    for(int i=0;i<=9;sta/=3,i++)
    {
        if((i&1)&&sta%3==1) return false;
        if(!(i&1)&&sta%3==2) return false;
    }
    return true;
}

int change(int sta,int i)
{
    int tt=(sta%cm[i+1])/cm[i];
    if(tt==2) return sta-cm[i];
    else return sta+cm[i];
}

ll dfs(int pos,int sta,bool pre,bool limit)
{
    if(pos==-1)
    {
        if(check(sta)) return 1;
        else return 0;
    }

    if(!pre&&!limit&&dp[pos][sta]!=-1) return dp[pos][sta];

    ll ans=0;
    int up=limit?x[pos]:9;

    for(int i=0;i<=up;i++)
        if(pre)
        ans+=dfs(pos-1,i==0?0:change(sta,i),i==0,limit&&i==x[pos]);
        else ans+=dfs(pos-1,change(sta,i),pre,limit&&i==x[pos]);

    if(!pre&&!limit) dp[pos][sta]=ans;
    return ans;
}

ll f(ll t)
{
    int pos=0;
    while(t)
    {
        x[pos++]=t%10;
        t/=10;
    }
    return dfs(pos-1,0,true,true);
}

int main(void)
{
    int t;
    scanf("%d",&t);
    memset(dp,-1,sizeof(dp));
    cm[0]=1;
    for(int i=1;i<11;i++)
        cm[i]=cm[i-1]*3;
    while(t--)
    {
        ll x,y;
        scanf("%lld%lld",&x,&y);
        printf("%lld\n",f(y)-f(x-1));
    }
    return 0;
}

七、Round Numbers POJ - 3252

题意:一个数x是round number,要求x写成二进制形式的时候数位上0的个数要大于等于1的个数,题目要求求出给定范围内的round number个数。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#define ll long long
using namespace std;
const int maxn=40;
int dp[maxn][maxn][maxn];
int x[maxn];
int dfs(int pos,int num0,int num1,bool pre,bool limit)
{
    int sum=0;
    if(pos==-1)
    {
        if(num0>=num1) return 1;
        else return 0;
    }


    if(!pre&&!limit&&dp[pos][num0][num1]!=-1) return dp[pos][num0][num1];
    int up=(limit?x[pos]:1);
    for(int i=0;i<=up;i++)
    {
        if(pre)
        sum+=dfs(pos-1,num0,i==1?num1+1:num1,i==0,limit&&i==up);
        else
        sum+=dfs(pos-1,i==0?num0+1:num0,i==1?num1+1:num1,pre,limit&&i==up);
    }
    if(!limit&&!pre) dp[pos][num0][num1]=sum;
    return sum;
}

int f(int t)
{
    ll pos=0;
    while(t)
    {
        x[pos++]=t%2;
        t/=2;
    }
    return dfs(pos-1,0,0,true,true);
}

int main(void)
{
    int n,m;
    memset(dp,-1,sizeof(dp));
    int t;

    while(scanf("%d%d",&n,&m)!=EOF)
    {
        int ans=f(m)-f(n-1);
        printf("%d\n",ans);
    }
    return 0;
}

八、Beautiful numbers CodeForces - 55D

题意:有T组询问,每次询问区间[l, r]中的beautiful number有多少。beautiful number是指这个数可以被组成它的数字整除。

题解:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#define ll long long
#define llu unsigned ll
using namespace std;
ll dp[20][50][2530];
int ha[2530];
int x[20];
int cnt=0;
int max_lcm;

ll gcd(ll a,ll b)
{
    if(b==0) return a;
    return gcd(b,a%b);
}

ll lcm(ll a,ll b)
{
    return a/gcd(a,b)*b;
}

void dfs(int now,int res)
{
    if(now==10)
    {
        ha[res]=1;
        return ;
    }

    dfs(now+1,lcm(res,now));
    dfs(now+1,res);
}

ll dfs(int pos,int now_lcm,int now_last,bool limit)
{
    ll sum=0;
    if(pos==-1)
    {
        if(now_last%now_lcm==0) return 1;
        else return 0;
    }

    if(!limit&&dp[pos][ha[now_lcm]][now_last]!=-1) return dp[pos][ha[now_lcm]][now_last];
    int up=limit?x[pos]:9;

    for(int i=0;i<=up;i++)
    {
        sum+=dfs(pos-1,i==0?now_lcm:lcm(now_lcm,i),(now_last*10+i)%max_lcm,limit&&i==up);
    }

    if(!limit)
        dp[pos][ha[now_lcm]][now_last]=sum;
    return sum;
}

ll f(ll t)
{
    int pos=0;
    while(t)
    {
        x[pos++]=t%10;
        t/=10;
    }
    return dfs(pos-1,1,0,true);
}

int main(void)
{
    ll x,y;
    dfs(1,1);
    for(int i=1;i<2530;i++)
    {
        if(ha[i]) ha[i]=++cnt;
    }
    max_lcm=1;
    for(int i=1;i<=9;i++)
        max_lcm=lcm(max_lcm,i);
    //cout<<max_lcm<<endl;
    //cout<<cnt<<endl;
    memset(dp,-1,sizeof(dp));

    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%I64d%I64d",&x,&y);
        printf("%I64d\n",f(y)-f(x-1));
    }
    return 0;
}

九、Classy Numbers CodeForces - 1036C

题意:所有数位上不为0的数不超过3个

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#define ll long long
#define llu unsigned ll
using namespace std;
int dp[20][4];int x[20];
ll dfs(int pos,int pre,int sta,bool limit)
{
    ll sum=0;
    if(pos==-1) return 1;
    if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];
    if(sta>=3) return dp[pos][sta]=1;

    int up=(limit?x[pos]:9);
    for(int i=0;i<=up;i++)
    {
        sum+=dfs(pos-1,i,i==0?sta:sta+1,limit&&i==up);
    }
    if(!limit) dp[pos][sta]=sum;
    return sum;
}

ll f(ll t)
{
    int pos=0;
    while(t)
    {
        x[pos++]=t%10;
        t/=10;
    }
    return dfs(pos-1,-1,0,true);
}

int main(void)
{
    ll n,m;
    int t;
    memset(dp,-1,sizeof(dp));
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld%lld",&n,&m);
        ll ans=f(m)-f(n-1);
        printf("%lld\n",ans);
    }
    return 0;
}

十、 Bomb HDU - 3555

题意:包含49这个数的数字个数。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#define ll long long
using namespace std;
ll dp[28][2][2];
ll x[20];
ll dfs(ll pos,ll pre,ll sta,bool limit)
{
    ll sum=0;
    if(pos==-1)
    {
        if(sta) return 1;
        else return 0;
    }
    if(!limit&&dp[pos][pre][sta]!=-1) return dp[pos][pre][sta];
    int up=(limit?x[pos]:9);
    for(int i=0;i<=up;i++)
    {
        sum+=dfs(pos-1,i==4,sta||pre&&i==9,limit&&i==up);
    }
    if(!limit) dp[pos][pre][sta]=sum;
    return sum;
}

ll f(ll t)
{
    ll pos=0;
    while(t)
    {
        x[pos++]=t%10;
        t/=10;
    }
    return dfs(pos-1,0,0,true);
}

int main(void)
{
    ll n,m;
    memset(dp,-1,sizeof(dp));
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld",&n);
        ll ans=f(n);
        printf("%lld\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值