51nod1042 数字0-9的数量(数位dp or 模拟)

博客围绕1042数字0 - 9的数量展开,介绍了两种计算方法。一是数位dp,需考虑当前数字个位对个位、高位对个位、个位对高位的影响;二是迭代,分开计算0 - 9每个数字出现的次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1042 数字0-9的数量

题目

在这里插入图片描述

分析

两种做法,

  • 数位dp,这种比较好理解
  • 还有迭代模拟
1. 数位dp

想好三点,
(例 2048)当前数字 e 是 204 时:

  1. 当前数字个位对个位影响 , 204 % 10 = 4, 【0,4】加上 s

  2. 当前数字高位对个位影响,204 / 10 = 20, 【0, 9】加上20 * s

  3. 当前数字个位对高位影响, 【2】加上 s * (4 + 1), 【0】加上 s * (4 + 1)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int M = 2e2 + 10;
const int N = 1e3 + 10;

ll arr[N], brr[N];  //存 0~9 出现次数

// s 代表当前数字的个位是 e 的哪一位,(个位,百位,千位……)
void dfs(ll s, ll e, ll c[])      // 1 到 e
{
    ll n = e / 10;                //高位
    ll m = e % 10;                //低位  当前位
    ll t = n;
    for(int i = 0; i <= m; i++){  //当前数字个位对个位影响
        c[i] += s;      
    }
    for (int i = 0; i < 10; i++){//当前数字高位对个位影响
        c[i] += s * n;
    }
    c[0] -= s;                   //去除前缀零
    while(t){                    //当前数字个位对高位影响
        c[t % 10] += s * (m + 1);
        t /= 10;
    }
    if(n){                      //n 已经处理过,从 n-1 递归
        dfs(s * 10, n - 1, c);
    }
}

int main()
{
    ll a, b;
    scanf("%lld%lld", &a, &b);
    dfs(1, a-1, arr);   //计算 1 到 a-1
    dfs(1, b, brr);     //计算 1 到 b
    for(int i = 0; i < 10; i++){
        printf("%lld\n", brr[i] - arr[i]);
    }
    return 0;
}
2. 迭代

分开计算0~9出现几次

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define d(x) cout << (x) << endl
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e2 + 10;

ll dp[N];   //18位
ll sum, len, cnt, tail, tnum;

ll cal(ll num, int x)     //从 1 到 a,x出现次数, x 属于0~9
{
    sum = len = tail = 0;
    cnt = 1;
    tnum = num;
    while(num){
        int tmp = num % 10;     //当前数字的个位
        len++;      
        if(tmp > x){            //
            sum += dp[len - 1] * tmp + cnt;                                                         
        }else if(tmp == x){
            sum += dp[len - 1] * tmp + tail + 1;
        }else{
            sum += dp[len - 1] * tmp;
        }
        tail += tmp * cnt;      //当前数字后面的数   eg:2048 , 当前 20  , tail 48
        cnt *= 10;              //进行到第几位
        num /= 10;    
    }
    if(!x){     //是 0 的话删除前缀零
        ll ans = 1;
        while(tnum){
            sum -= ans;
            ans *= 10;
            tnum /= 10;
        }
    }
    return sum;
}

int main()
{
    for(int i = 1; i <= 18; i ++) {         // 0 1 20 300 4000 50000 600000
        dp[i] = dp[i-1]*10 + pow(10,i-1);
    }
    ll a, b;
    cin >> a >> b;
    for (int i = 0; i < 10; i++){
        cout << cal(b, i) - cal(a - 1, i) << endl;
    }
        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值