51Nod 1042 - 数字0-9的数量(计数/数位DP)

题目链接 https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1042

【题目描述】
给出一段区间 [a,b],统计这个区间内0-9出现的次数
比如 10-19,1出现11次(10,11,12,13,14,15,16,17,18,19,其中11包括2个1),其余数字各出现1次

Input
两个数 a , b 1 <= a <= b <= 10 18 )

Output
输出共10行,分别是0-9出现的次数

Input示例
10 19

Output示例
1
11
1
1
1
1
1
1
1
1

【思路】
51Nod 1009的进阶版,可以计数,可以数位DP,注意0比较特殊,因为前导0是不能被计算在内的,下面是计数直接计算

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

ll a, b;
ll c[20];

ll pow(ll x, int n)
{
    ll res = 1;
    while (n)
    {
        if (n & 1) res *= x;
        x *= x;
        n >>= 1;
    }
    return res;
}

void init()
{
    memset(c, 0, sizeof(c));
    for (int i = 1; i <= 18; i++)
        c[i] = i*pow(10, i - 1);
}

ll solve(ll x, int num)//返回0~x中数字num出现的次数
{
    ll res = 0, copy = x;
    ll total = 0, po = 1;//total不断求和,po表示当前的幂
    int len = 0;//由低位向高位求到的当前某一位
    int dig;//当前位

    while (x)
    {
        dig = x % 10;
        x /= 10;
        len++;

        if (dig < num) res += dig*c[len - 1];
        else if (dig == num) res += dig*c[len - 1] + 1 + total;
        else res += dig*c[len - 1] + po;

        total += dig*po;
        po *= 10;
    }

    if (0 == num)
    {
        ll temp = 1;
        while (copy)
        {
            res -= temp;
            temp *= 10;
            copy /= 10;
        }
        res++;
    }

    return res;
}

int main()
{
    init();
    while (~scanf("%lld%lld", &a, &b))
    {
        for (int i = 0; i <= 9; i++)
            printf("%lld\n", solve(b, i) - solve(a - 1, i));
    }
    return 0;
}

数位DP

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

ll pw[20];
int a[20];
ll dp[20][15];

ll dfs(int pos,int pre,int k,bool lead,bool limit){
    if(pos==-1){
        if(!lead || k!=0) return pre==k?1:0;
        else return 0;
    }
    if(!lead && !limit && dp[pos][pre]!=-1) return dp[pos][pre];
    int up=limit?a[pos]:9;
    ll ans=0;
    for(int i=0;i<=up;++i){
        if(pre==k){
            if(k!=0 || !lead){
                if(i==up && limit){
                    ll tmp=0;
                    for(int j=pos-1;j>=0;--j){
                        tmp=tmp*10+a[j];
                    }
                    ans+=tmp+1;
                }
                else{
                    ans+=pw[pos];
                }
            }
            ans+=dfs(pos-1,i,k,i==0 && lead,i==up && limit);
        }
        else{
            ans+=dfs(pos-1,i,k,i==0 && lead,i==up && limit);
        }
    }
    if(!lead && !limit) dp[pos][pre]=ans;
    return ans;
}

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

int main(){
    pw[0]=1;
    for(int i=1;i<=18;++i) pw[i]=10*pw[i-1];
    ll le,ri;
    scanf("%lld%lld",&le,&ri);
    for(int k=0;k<=9;++k){
        memset(dp,-1,sizeof(dp));
        ll x=solve(ri,k);
        memset(dp,-1,sizeof(dp));
        ll y=solve(le-1,k);
        printf("%lld\n",x-y);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xiao_k666/article/details/82148297