AcWing 338. 计数问题 题解 数位统计DP

这篇博客介绍了如何计算在指定范围[1, n]内,数字i出现的次数。通过分情况讨论和编程实现了一个count函数,用于统计1到n的整数中数字i的出现次数。文章详细解释了算法思路,包括对每个数位的遍历和不同边界条件的处理,并给出了C++代码示例。

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

题目

在这里插入图片描述


思路

  • 分情况讨论
    [a,b],0−9[a,b],0-9[a,b],09
    我们可以实现一个count函数:
    count(n,x)count(n,x)count(n,x),1~n中x出现的次数
    最后的答案就是count(b,x)−count(a−1,x)count(b,x)-count(a-1,x)count(b,x)count(a1,x)
  • 举例:
    1~n,x=1
    n=abcdefg
    分别求出1在每一位上出现的次数
    求1在第4位上出现的次数:
    1<=xxx1yyy<=abcdefg
    (1) 当xxx=000-(abc-1),yyy=000-999
    总共有abc×1000abc\times1000abc×1000个数会在第4位上出现1
    (2)当xxx=abc
    2.1)当d<1,abc1yyy>abc0efg
    这种情况不会有数在第4位上出现1
    2.2)当d=1,0<=yyy<=efg
    这种情况下会有efg+1efg+1efg+1个数会在第4位上出现1
    2.3)当d>1,0<=yyy<=999
    这种情况下会有1000个数会在第4位上出现1

所以该题的思路是:

  • 1)对每个a,b,在进一步处理前保持a<b(必要时,swap一下)
  • 2)实现一个函数count(n,i):计算1~n的是所有数中,数字i出现的次数
    在count函数中:
    算出整数n的总位数d(便于后面遍历)
    从低位开始往高位遍历每一位:
    取出当前遍历位的数dj
    算出:以dj为分界,其左边的数的大小l,10的其右边的位数次方p,右边的数的大小r
    需要的数据都有了,下面就可以分情况讨论计算结果了。
  • 3)在分情况讨论时,要格外注意当左边的数l为0的情况,以及当前的i为0的情况当i为0时其左边整数不能为0

代码

摘自:https://www.acwing.com/solution/content/7128/
yyds!

#include<iostream>
#include<cmath>
using namespace std;

int dgt(int n)  //计算整数n有多少位
{
    int res=0;
    while (n) 
	{
		res++;
		n/=10;
	}
    return res;
}

int count(int n,int i)     //计算从1到n的整数中数字i出现多少次 
{
    int res=0,d=dgt(n);
    for(int j=1;j<=d;j++)     //从右到左第j位上数字i出现多少次(从右往左遍历)
    {
        // l和r是第j位左边和右边的整数 (视频中的abc和efg); dj是第j位的数字; 
        int p=pow(10,j-1),l=n/p/10,r=n%p,dj=n/p%10;
        // 计算第j位左边的整数小于l (视频中xxx = 000 ~ abc - 1)的情况
        if(i) res+=l*p;  //i如果不是0的话,直接算 
        if(!i&&l) res+=(l-1)*p; // 如果i = 0, 左边高位不能全为0(视频中xxx = 001 ~ abc - 1)
        // 计算第j位左边的整数等于l (视频中xxx = abc)的情况
        if((dj>i)&&(i||l)) res+=p;
        if((dj==i)) res+=r+1;
    }
    return res;
}

int main()
{
    int a, b;
    while(cin>>a>>b,a)    //a不等于0 
    {
        if(a>b) swap(a,b);   //如果a比b大,交换a和b 
        for(int i=0;i<=9;i++) 
			cout<<count(b,i)-count(a-1,i)<<' ';
        cout<<endl;
    }
    return 0;
}
  • if((dj>i)&&(i||l)) res+=p;
    在这里插入图片描述
  • if((dj==i)) res+=r+1;
    在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alkali!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值