算法学习:1.统计数字问题

分析别人的代码,希望能够提升自己吧


问题描述

(1)、问题描述
一本书的页码从自然数1 开始顺序编码直到自然数n。书的页码按照通常的习惯编排,每个页码都不含多余的前导数字0。例如,第6 页用数字6 表示,而不是06 或006 等。数字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1, 2,…,9。
(2)、算法设计
给定表示书的总页码的10 进制整数n (1≤n≤10 ) 。编程计算书的全部页码中分别用到多少次数字0,1,2,…,9。


一、暴力破解法

暴力破解法也就是直接法,根据题目要求,可以设定两个循环,最外层循环来遍历1~n,内层循环对每个数进行位数拆分,设定一个次数数组用来记录拆分后每位出现的次数,最后打印即可。

#include <iostream>
#include <cstdio>
#include <string>

using namespace std;
int main ()
{
    int c[10] = {0};                //记录次数数组
    int n;
    cin >> n;
    for(int i = 1 ; i <= n ; i++)
    {
        int tmp = i;
        while(tmp != 0)
        {
            c[tmp%10]++;            //位数拆分加记录
            tmp/=10;
        }
    }
    for(int i = 0 ; i <= 9 ; i++)
    {
        cout << c[i] << endl;
    }
    return 0;
}

二、数字分析法(递归)

数字分析法是通过分析0 - 9 , 00 - 99 , 000 - 999 …等规律的数字中每位数字出现的次数(每位数字出现的次数相同),进而将任意的数字拆分为规律数字的多个区间与剩余数字,而剩余数字又可以进行拆分,其中包含了递归的思想

1.对特征数字进行分析

在这里插入图片描述
下方的公式为特征数字的递推公式,尤其注意f(n)指的是n位数字中每个数字出现的次数(单指一个数字,并不是总和)

有了这个基础现在可以对任意的数字进行区间划分
在这里插入图片描述
以上计算出的每位出现的数字次数,其中零中包含了前导零。下面为减去前导零的计算。

2.减去前导零

用例子说明
在这里插入图片描述

3.代码实现

传送门:https://blog.csdn.net/qq_40054352/article/details/88594932?spm=1001.2014.3001.5506
这位大佬无论是解释还是代码都阐述的十分详细,在此我借用大佬的代码,写了一些注释,便于自己理解。

#include <iostream>
#include <cmath>

using namespace std;

int c[10];
int get_wei(int n)          //获得数字n的位数
{
    return int(log10(n))+1;
}

int get_max(int n)          //获得数字n最高位的数字
{
    return n/int(pow(10,get_wei(n)-1));
}

int get_yu(int n)           //获得数字相对于最高位的余数,例如4321则获得321,321则获得21
{
    return n%int(pow(10,get_wei(n)-1));
}

int get_zero(int m)         //获得m位数中零的个数
{
    if(m == 1)
    {
        return 1;
    }
    return get_zero(m-1)+int(pow(10,m-1));
}

void solve(int n)           //解决函数
{
    for(int i = 0 ; i < 10 ; i++)       //计算除最高位之外内位中数字出现的次数
    {
        c[i] = c[i] + get_max(n) * (get_wei(n)-1)*int(pow(10,get_wei(n)-2));
    }
    for(int i = 0 ; i < get_max(n) ; i++)   //计算最高位除最大数字之外其余数出现的次数(约束条件之中没有'=')
    {
        c[i] = c[i] + int(pow(10,get_wei(n)-1));
    }

    c[get_max(n)] = c[get_max(n)] + get_yu(n)+1;    //加上最高位最大数出现的次数



    //以下为处理特殊情况
    int t = get_yu(n);
    if(t == 0){                         //余数为零情况
        c[0]+=get_wei(n)-1;
        return ;
    }
    int len = log10(t)+1;
    if(len!=get_wei(n)-1){              //余数的位数小于最大位数-1的情况
        c[0] += (get_wei(n)-len-1)*(t+1);
    }
    return solve(t);                //递归调用解决余数的出现次数
}

int main ()
{
    int n;
    bool flag = true;
    while(flag)
    {
        for(int i = 0 ; i < 10 ; i++)
        {
            c[i] = 0;
        }
        cout << "请输入一个页码。以0结束程序:" << endl;
        cin >> n;
        if(n == 0)
        {
            flag = false;
        }
        solve(n);
        c[0] = c[0] - get_zero(get_wei(n));             //前导零处理
        cout << "数字0-9出现的次数分别为:" << endl;
        for(int j = 0 ; j < 10 ; j++)
        {
            cout << c[j] << " ";
        }
        cout << endl << endl;
    }
    return 0;
}
————————————————
版权声明:本文为CSDN博主「凛冬&已至」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_40054352/article/details/88594932

再次感谢大佬的代码,受益匪浅。

  • 15
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值