《编程之美》“1的数目”的另一个解法

问题:给定一个十进制正整数N,写下从1开始,到N的所有整数,然后数一下其中出现的所有“1”的个数f(N)。

书上给的最优解,考虑十进制表示的每一位,对于0,1,其他这三种情况分开讨论,然后结合高位数字、当前位数字、低位数字计算。

我想到的是另一个解法,时间复杂度一样,但思路更简单一些:

   1
   2
   3
....
  10
  11
  12
....
 100
 101
 102
....
1000
1001
1002

个位数的1:每10个有1个

十位数的1:每100个有10个

百位数的1:每1000个有100个

千位数的1:每10000个有1000个

...

逐位统计。例如百位数上的1,先看有几个整千,计算100*(N/1000)。然后考虑剩余的不到1000的部分,从100算起有几个(注意不到100的不算,大于等于200的也不算)。两部分加起来即可。

C++17代码实现如下:

#include <algorithm>

int
f(int n) { int iCount = 0; int iFactor = 1; while (n / iFactor != 0) { int iFactorH = iFactor * 10; iCount += iFactor * (n / iFactorH) + std::clamp(n % iFactorH - iFactor + 1, 0, iFactor); iFactor *= 10; } return iCount; }

结果验证过是一致的。

顺便打印了一下所有f(N)=N的案例:

1
199981
199982
199983
199984
199985
199986
199987
199988
199989
199990
200000
200001
1599981
1599982
1599983
1599984
1599985
1599986
1599987
1599988
1599989
1599990
2600000
2600001
13199998
35000000
35000001
35199981
35199982
35199983
35199984
35199985
35199986
35199987
35199988
35199989
35199990
35200000
35200001
117463825
500000000
500000001
500199981
500199982
500199983
500199984
500199985
500199986
500199987
500199988
500199989
500199990
500200000
500200001
501599981
501599982
501599983
501599984
501599985
501599986
501599987
501599988
501599989
501599990
502600000
502600001
513199998
535000000
535000001
535199981
535199982
535199983
535199984
535199985
535199986
535199987
535199988
535199989
535199990
535200000
535200001
1111111110

并不是很多,而且可以看到有抱团的现象。书中证明了这个集合是有限的,最大值就是1111111110。

posted @ 2020-07-05 22:04  Xrst  阅读(195)  评论(0编辑  收藏  举报