求从1到n这n个整数的十进制表示中1出现的次数

该博客介绍了一个算法,用于计算从1到n所有整数中数字1出现的次数。通过分析数字的百位、十位和个位,提出了一种通用的计算公式,并考虑了数字0的特殊情况。

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

题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。例如输入12,从1到12这些整数中包含1 的数字有1,10,11和12,1一共出现了5次。

解: 由题意可知,所求的1的数量,就是1在数字n中的各个位数上出现的次数。
所以,设数n为abc,求1在abc三个位置数出现的次数。
     求c位:当c>1时,ab位为00..ab,即c位1的出现次数为ab+1;
            当c=1时,ab位为00..ab,即c位1的出现次数为ab+1;
            当c<1时,ab位为00..ab-1,即c位1的出现次数为ab;
     求a位:当a>1时,bc位为00..99,即a位1的出现次数为100;
            当a=1时,bc位为00..ab,即c位1的出现次数为bc+1;
            当a<1时,bc位为00,即c位1的出现次数为0;
     求b位:当b>1时,a位0..a,c位0..9,即1的次数(a+1)*10;
            当b=1时,a位0..a, 其中
                        a位0,c位只能是0..c,-> c+1次
                        a位1..a时,c位为0..9,->a*10次
                即1的次数为a*10+c+1
            当b<1时,a位1..a,c位0..9,即1的次数位a*10

由上可看出,计算中间位是最复杂的,左右俩边都是很简单的。那么能不能把计算俩边的算式和计算中间位一样呢。试试看:
设左边位为left, 右边位为right, 中间位为mid, 中间位就是计算次数的当前位置, 1的次数为sum
当mid=b时,left=a,right=c
        则:mid>1 -> sum=(left+1) * 10^1
            mid=1 -> sum=left * 10^1 + right + 1
            mid<1 -> sum=left * 10^1
当mid=c位时,left=ab,right=0
        则:mid>1 -> sum=(left+1) * 10^0;
            mid=1 -> sum=left * 10^0 + right +1;
            mid<1 -> sum=left * 10^0;
当mid=a时,left=0, right=bc
        则:mid>1 -> sum=100=10^2=(left+1) * 10^2;
            mid=1 -> sum=right+1=left * 10^2 + right + 1;
            mid<1 -> sum=0=left * 10^2;
显然abc三个位置的计算算式可以是一样的,只数字所在的位置的指数不一样,百位2,十位1,个位0

int count_digital(int digit, int tag=1){
        int left, mid, right;
        int sum = 0;
        int n = 1;

        while (digit / n > 0){
                right = digit % n;
                left = digit / (n * 10);
                mid = digit / n % n;

                if (mid > tag){
                        sum += (left+1) * n;
                } else if (mid == tag){
                        sum += left * n + right + 1;
                }  else { //if (mid < tag) {
                        sum += left * n;
                }
                n *= 10;
        }

        return sum;
}

考虑计算数字0的次数情况:
当tag=0时,是对mid==0进行计次,此时left不能出现0的情况。例如对102进行这种计算,则百位1不可能出现0的情况,因为001..099,这个0已经不符合题意了。对十位进行0的次数计算时,left只能取1,left取0也不符合题意了。
所以以上代码的运行结果会错误。
所以添加判断条件flag

int count_digital(int digit, int tag=0){
        int left, mid, right;
        int sum = 0;
        int flag = 0;
        int n = 1;
       
        if (0 == tag) {
                flag = -1;
        }
        while (digit / n > 0){
                right = digit % n;
                left = digit / (n * 10);
                mid = digit / n % n;

                if ((tag==0) && (left==0)) break;
                if (mid > tag){
                        sum += (left+flag+1) * n;
                } else if (mid == tag){
                        sum += (left+flag) * n + right + 1;
                }  else { //if (mid < tag) {
                        sum += left * n;
                }
                n *= 10;
        }

        return sum;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值