我怎样才能找到qt。数字包括段[1..n]中的1? 例如:对于n = 29,它是12;对于n = 100,它是20。 n可达到10 ^ 9,时限为2秒。 语言C ++
答案 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
方法可以将大多数计算变量吹出水中: - )