• BZOJ1833:[ZJOI2010]数字计数——题解


    http://www.lydsy.com/JudgeOnline/problem.php?id=1833

    https://www.luogu.org/problemnew/show/P2602

    给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。

    虽说是数位dp,但是用套路写的话蛮麻烦的……于是嫖了一篇记忆化搜索:https://www.cnblogs.com/Sakits/p/6815468.html

    大致解释一下dfs的变量,p为当前填到哪里,num为数码,sum为当前答案。

    lead为是否为数的第末位(就是数要比上限的长度短的情况)。

    limit为是否卡着数的上限(如果你卡着上限的话你在枚举数码填进去的时候就会受到限制)。

    我们记忆化仅仅当lead和limit均为0才行。

    #include<cstdio>
    #include<iostream>
    #include<queue>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<cctype>
    using namespace std;
    typedef long long ll;
    const int N=15;
    ll f[N][10][N],a[N];
    ll dfs(int p,int num,int sum,bool lead,bool limit){
        if(!p)return sum;
        if(!limit&&!lead&&f[p][num][sum]!=-1)return f[p][num][sum];
        int maxx=limit?a[p]:9;
        ll ans=0;
        if(!lead||p==1)ans+=dfs(p-1,num,sum+(num==0),0,limit&&!a[p]);
        else ans+=dfs(p-1,num,sum,1,limit&&!a[p]);
        for(int i=1;i<=maxx;i++)
        ans+=dfs(p-1,num,sum+(num==i),0,limit&&a[p]==i);
        if(!limit&&!lead)f[p][num][sum]=ans;
        return ans;
    }
    ll dp(ll x,int y){
        int len=0;
        while(x)a[++len]=x%10,x/=10;
        if(!len)return y==0?1:0;
        return dfs(len,y,0,1,1);
    }
    int main(){
        ll a,b;
        scanf("%lld%lld",&a,&b);
        memset(f,-1,sizeof(f));
        for(int i=0;i<9;i++)printf("%lld ",dp(b,i)-dp(a-1,i));
        printf("%lld
    ",dp(b,9)-dp(a-1,9));
        return 0;
    }

    +++++++++++++++++++++++++++++++++++++++++++

     +本文作者:luyouqi233。               +

     +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

    +++++++++++++++++++++++++++++++++++++++++++

  • 相关阅读:
    vue自定义指令使用注意事项
    es6新增方法---实用
    webpack和gulp的区别
    OSI 5层协议,socket,从协议角度看网络通信
    网络通信流程
    数据相关的模块
    一些模块
    面向对象
    ATM作业
    XML模块增删改查基本操作
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/8533724.html
Copyright © 2020-2023  润新知