关于c语言中的经典页码问题

链接:https://ac.nowcoder.com/acm/problem/15327
来源:牛客网

题目描述

小明前几天看书看累了,脑海中突然闪过,这书的页码也很可爱啊。一本书的页码从自然数1按自然顺序编码到n.每个页码不会含有多余的前导数字0.例如,第6页用数字6表示,而不是06、006表示。下面问题来了,你能帮忙小明解决以下问题:给定总页码n,计算出书的全部页码中分别用到的多少次数字0,1,2,...,9.

输入描述:

输入书的总页码一个整数n。

输出描述:

输出总共10行。在第k行输出页码中用到数字k-1的次数。(k=1,2,...,10)


一开始,我是直接暴力解法,把每个数的每一位得出来的数字依次相加,设一个数组来记录0到9的数字个数,然后牛客说我超时了,用dev的时候发现在1e9的时候甚至要27s。想了很久没想明白,之后我借鉴了一个大佬的想法,直接算每一个数字的个数,
通过一些规律来计算。

下面是我对那个思路的一些理解

我们先设置一个数,以11459为例

先从1开始,从个位开始数,1,2,3,4,5,6,7,8,9,10每十位就有一个数的个位上是1,那么11459/10=1145......9,一共有1145个10,就有1145个1。由于还余了9,注意9大于1,那么还有11450,11451,11452...11459,
这九个数没算,还少了一个1,我们就还要加一个1,所以个位上一共有1146个1;

现在从十位开始,11,12,13,14,15,16,17,18,19,20,...,99每百位就有十个数的十位上是1,那么11459/100=114.....59,一共有114个100,就有114*10个1。由于还余了59,注意5大于1,那么还有11410,11411,
...,11419没算,还少了十个1,我们就还要加10个1,所以十位上一共有1150个1;

从百位开始,也遵循上面的思想,11459/1000=11......459,4大于1,所以百位上有11*100+100个1,即1200个1;

从千位开始,对于1来说就有些特别了,因为11459/10000=1......1459,所以有1*1000个1,因为1等于1,所以还有11000,11001,11002...,11459上的千位上的1没有数,从0开始到459有460个数,那就要再加460,
即1460个1;

万位也是如此,11459/100000=0......11459,所以有0*10000个1,1等于1,就有10000,10001,10002...,11459上的万位上的1没有数,从0到1459有1460个数,那就要再加1460个1;
最后一共有1146+1150+1200+1460+1460=6416个1;

同理2,3,4,5...,9也是这么算
对于6为例
个位11459/10=1145...9>6 1145*1+1;
十位11459/100=114...59<60 114*10;
百位11459/1000=11...459<600 11*100;
千位11459/10000=1...1459<6000 1*1000;
万位11459/100000=0...11459<60000 0*10000;
一共有1146+1140+1100+1000+0=4386个6;

再来考虑特殊的0,还是以11459为例

从个位开始,11459/10=1145...9>0 ,但此时不能单纯的用1145*1+1了,因为11450是被计算到那1145个0里面了
(你看1,2,3,4,5,6,7,8,9,10,在出现0的时候进位了,那么这个0在11459/10=1145时就被计算到了)
所以个位上有1145*1个0;
十位,11459/100=114...59>10 114*10;
百位,11459/1000=11...459>100 11*100;
千位,11459/10000=1...1459>1000 1*1000;
万位,11459/100000=0...11459>10000 0*10000;
一共有1145+1140+1100+1000+0=4385个0;

下面是自己写的代码

#include <stdio.h>

long sum1(long n, int i)

{
  long sum = 0, m, s = 1, panduan;
  int k = n;
  if (i == 0)

  {
    while (k > 0)

    {
      m = k / 10;//每n个里面有0.1n个零(n大于或等于10且个位为零)
      sum += m * s;//计算总和
      s *= 10;//从个位开始计算

          //从个位开始计算

            //个位上每十个有一个零

           //十位上每百个有十个零
                                //......
                                //直到末位
                                k /= 10;//算完个位算十位,依次下去

                 }
  }
  else

  {
    while (k > 0)

    {
      m = k / 10;//每n个里面有0.1n个i(n大于或等于10且个位为零)
      sum += m * s;//计算总和
      panduan = k % 10;//判断是否有少加的数字
      if (panduan > i) {
      sum += s;//比如9大于5,那如果是个位上就少算一个
      //十位上就少算10个
    }
    if (panduan == i)

    {
      sum += n - k * s + 1;//如11459,在千位上是1等于1;
      //则从1000到1459有460个千位的1没算;
      //从10000到11459有1460个万位的1没算;
    }
    s *= 10;//从个位开始计算
    //个位上每十个有一个零
    //十位上每百个有十个零
    //......
    //直到末位
    k /= 10;//算完个位算十位,依次下去
    }
  }
  return sum;
}

int main()

{
  long n, i, m;
  scanf("%ld", &n);
  for (i = 0; i <= 9; i++)

  {
    m = sum1(n,i);
    printf("%ld\n", m);
  }
  return 0;
}

















posted @ 2022-09-15 09:56  彼岸的约定  阅读(313)  评论(0编辑  收藏  举报