包含1的数字

时间:2011-02-01 13:44:39

标签: c++ algorithm

我怎样才能找到qt。数字包括段[1..n]中的1? 例如:对于n = 29,它是12;对于n = 100,它是20。 n可达到10 ^ 9,时限为2秒。 语言C ++

4 个答案:

答案 0 :(得分:12)

检查单个数字是否计算其模数10的简单方法:如果它是1那么它确实如此,否则将其除以10并重复。如果数字为0,则不包括1.

这应该足以让你开始。

答案 1 :(得分:4)

聪明的事情是创建一个公式,通过递归证明它,然后应用它。

实际上我最初不会使用递归,而是使用一些简单的数学:虽然这里没有随机事件,但却有一些概率/组合理论。

假设有两个数字表示两个,那么下面没有任何1的数字总是9到数字的幂,因此对于00到99,这是81个这样的数字。因此有19个(100-81)。

在每种情况下,我们都会计算不包含任何数字的数字。

例如,5394是0到4999范围的总和以及范围5000到5394的总和。

总和到4999是数字的乘积:4 * 9 * 9 * 9(0-4,0-9,0-9,0-9,每种情况除外1)

总和5000到5394与000到394的总和相同:分割000到299,300到394.因此2 * 9 * 9 +从00到94运行,这是8 * 9加90-94,这是还有4个。

所以从0到5394有4 * 9 * 9 * 9(2916)+ 2 * 9 * 9(162)+ 8 * 9(72)+ 4 = 3154

从我们的主数字+ 1(5395)减去我们得到2241的解。(我们从5395减去因为我们从0到5394计算,这是5395个数字)

解决方案是将每个数字> 1,减去1,然后在其后面出现的每个数字乘以9。如果数字是1,我们不减去一个。如果数字为零,我们跳过。

如果我们遇到1,我们会跳过所有剩余的数字。因此,如果我们的数字是52136,在达到1之后我们跳过所有其余的。 (我们的计数将达到52099,之后就没有了)。我们仍然使用整数进行减法。

对于最后一位数字:如果到目前为止还没有1位数字,我们将最后一位数字加入,如果是0,则加1。因此,为5394添加4,为5390添加1,没有对于5216,因为它在最后一个数字之前包含1但仍然为5391添加1(表示5390未包括在先前的计数中)。

然后从我们的数字+ 1减去这个总和再减1(因为我有0到N的范围是N + 1个数字)

n = 29因此1 * 9 + 9 = 18.从n + 1减去我们得到12就像你想要的那样。

n = 100:1 * 9 * 9 = 81.减去101(我们的数字包含1)得到20。

有你的公式。现在去编程吧。

**略有替代方式**

以“递归”的方式:我们计算不包含1的数字的数量严格小于我们的值(不包括它)让我们称之为名称,f(N)

f(10N)总是f(N)* 9.我们可以很容易地证明这一点:有小于N的f(N)个数然后将它们乘以10并加上每个数字0和2-最后9个,9个不同的数字。

f(10N + m)其中0 <= m <= 9是单个数字使用“最后一个数字添加”公式,因此如果N包含1个数字,则它只是f(N)* 9否则为m添加0对于m = 1,= 0 1,对于任何其他,m = 1.

f(394858)= 7 + 9 * f(39485) f(573940)= 9 * f(57394) f(491029)= 9 * f(49102)(包含1位数)

您的“f”函数可以递归并且必须包含2条信息:它是否包含1位数字和返回值。基本案例是单个数字

f(N)是严格小于N且不包含1的数字的数量。要获得该数字,请从N中减去该数字,如果我们的数字本身包含1,则加1。

f(10N + M)= 9 * f(N)如果N包含1,9 * f(N)+ f(M),如果N不包含1.

f(0)= 0 f(1)= 1 f(M)为2至9是M-1 f(10)= 9 * f(1)= 9 f(100)= 9 * f(10)= 81 解决方案是100 - 81 + 1(因为100包含1我们添加一个),这给我们20 f(9)= 8 f(99)= 8 * 9 + 8 = 80 从99减去80给我们19并且不添加1,因为99不包含1。 现在让我们尝试编码:

struct res 
{
  unsigned int numDigs;
  bool hasOneDig;

  res( unsigned int n, bool h ) : numDigs( n ), hasOneDig( h ) {}
};

res oneDigitCount( unsigned int input )
{
   assert( input < 10 );

   switch( input )
   {
      case 0: 
         return res( 0, false );
      case 1:
         return res( 1, true );
      default:
         return res( input-1, false );
   }
};

res countNoOnes( unsigned int input )
{
   if( input < 10 )
        return oneDigitCount( input ); // base case that ends recursion

   unsigned int quotient = input / 10;
   unsigned int remainder = input % 10;

   res result = countNoOnes( quotient ); // recursive
   result.numDigs *= 9;

   if( !result.hasOneDig )
   {
       res remainderRes = oneDigitCount( remainder );
       result.numDigs += remainderRes.numDigs;
       if( remainderRes.hasOneDig ) // or remainder==1
          result.hasOneDig = true;
   }

   return result;
}

unsigned int countNumsContainingOne( unsigned int input )
{
   res noOnes = countNoOnes(input);
   unsigned int result = input - noOnes.numDigs;
   if(noOnes.hasOneDig)
     ++result; // as this number has a one

   return result;
}

不是非常OO,可以轻松地适应C(用返回结构的函数替换构造函数)但是应该可以工作。

答案 2 :(得分:2)

对于每个数字,请执行以下操作:为从1到n的所有数字创建外部循环。

您可以printf()将其作为字符串缓冲区(例如char[10]),然后检查相同的字符串缓冲区,如果包含带有简单while循环的“1”。

我会把实现留给你:)

答案 3 :(得分:2)

由于它是标记的作业,我不会给你C ++代码,因为你应该做一些的工作: - )

您可以将其分解为两个问题,第一个问题是如何判断给定数字是否包含一个问题。这有一个优雅的递归解决方案:

def containsOne (n):
    if n <= 0:
        return 0
    if n % 10 == 1:
        return 1
    return containsOne (n / 10)

如果任何数字为零或更少,则会返回0,如果包含1,则返回1。它通过递归检查1的最低有效数字来完成此操作。如果是,则返回1,否则它将数字除以10(整数除法,如472成为47)并继续。

然后你有一个简单的迭代函数来计算:

def countOfOneNumbersOneTo(n):
    count = 0
    for i = 1 to n:
        count = count + containsOne(i)
    return count

这是关于最简单的代码,可以解决问题。


您可以在以下Python代码中看到它的实际效果(与语言一样接近伪代码):

import sys

def containsOne (n):
    if n <= 0:
        return 0
    if n % 10 == 1:
        return 1
    return containsOne (n / 10)

def countOfOneNumbersOneTo(n):
    count = 0
    for i in range(1,n+1):
        count = count + containsOne(i)
    return count

print countOfOneNumbersOneTo (int (sys.argv[1]))

和成绩单:

$ python qq.py -10
0

$ python qq.py 0
0

$ python qq.py 1
1

$ python qq.py 9
1

$ python qq.py 10
2

$ python qq.py 29
12

$ python qq.py 100
20

C版本如下:

#include <stdio.h>
#include <stdlib.h>

static int containsOne (int n) {
    if (n <= 0)
        return 0;
    if (n % 10 == 1)
        return 1;
    return containsOne (n / 10);
}

static int countOfOneNumbersOneTo (int n) {
    int i, count = 0;
    for (i = 1; i <= n; i++)
        count = count + containsOne(i);
    return count;
}

int main (int argc, char *argv[]) {
    printf ("%d\n", countOfOneNumbersOneTo (atoi (argv[1])));
    return 0;
}

如果您想要原始速度,一个选项是预先计算所有值并将结果写入文件。初始过程会有点慢,但是,一旦将值写入固定长度记录文件,查找就是一个简单,相对较快的seek/read操作。

对于数字10 9 ,这需要花费8分多钟才能完成旧旧的笔记本电脑,你需要记住,你不需要为每个数字执行此操作,因为您已经确定100有20个结果,您只需检查101并添加1(因为101有1)到20个。同样,知道{{1}得到1468559结果,检查1999999并加0(因为它没有1)。

我之前使用过这个技巧的文件包含数以百万计的素数。将其转换为文件中的位掩码,我有一个2000000方法可以将大多数计算变量吹出水中: - )