快速求解1~n的每个数字出现的次数.

来源:互联网 发布:mysql主从好处 编辑:程序博客网 时间:2024/04/30 11:52

对于快速求解1~n中每个数字出现次数的问题:

在做奥数题时,我们很多时候都会遇到这类问题:在1~999页中数字"1"出现了几次,数字"2"出现了几次...

对于这类问题在用笔算时我们一般是把1~999分为几个阶段:

1~9

10~99

100~999

...

10^n~10^n*10-1

这样子一个阶段一个阶段的求解,可以有效的快速的解答出来,然而在编程中,遇到这类问题,我们一般是用暴力枚举的方法,把1~999的每个数字都用字符串,然后再在字符串里计算tot。但是这样子的效率不高,O(n)的算法,使得在很多时候我们不能体现出计算机的最强大的一方面,当然如果数据范围大一点,O(n)也是不行的,所以今天,我要讲解一下如何用最简单的方法求出最快速的答案。


我们先想一想在暴力枚举中,我们的思路是与奥数不同的,而我们是否可以把思路转向另一侧,用奥数的方法解答出来呢?

例子1:

求出1~999里“1”出现的次数。

这道题首先我们现向奥数的方法划为三个阶段,然后计算1在个位,在十位,在百位出现的次数。

很明显:

个位

在100~999这个阶段里'1'在个位出现了[1..9],[0..9],[1]次,9*10=90次。

在10~99这个阶段里'1'则出现了[1..9],[1]次,9*1=9次。

在1~9这个阶段里'1'出现了1次。

十位:

在100~999这个阶段里'1'在十位出现了[1..9],[1],[0..9]次,9*10=90次。

在10~99这个阶段里'1'在十位出现了[1][0..9]次,1*10=10次。

百位:

在100~999这个阶段里'1'在百位共出现了[1][0..9][0..9],10*10=100次。

所以:在1~999个数当中,数字“1”出现了90+9+1+90+10+100=300次。


至此,我们已经能大概判断出有什么规律了,但是还不是非常准确,我们再看一个例子。


例子2:

求出1~684里“1”出现的次数。

同样划分阶段,再计算:

个位:

在100~684这个阶段里1在个位出现了[1..5],[0..9],[1]+[6],[0..8],[1]=5*10+1*9=59次。

在10~99这个阶段里1在个位出现了[1..9],[1]次,1*9=9次。

在1~9这个阶段里1在个位毋庸置疑出现了1次。

十位:

在100~684这个阶段里1在十位出现了[1..6],[1],[0..9]=6*10=60次。

在10~99这个阶段里1在十位同样出现了10次。

百位:

在100~684这个阶段里1在百位出现了[1],[0..9],[0..9]=10*10=100次。

所以:在1~684页中,数字“1”出现了59+9+1+60+10+100=239次。


从这里两个简单的例子我们可以发现:在计算个位在十、百位数的次数的时候,其实就等于这个数的前两位

计算1:

例如计算‘1’在个位出现次数:

       999:(90)+(9)=99

       684:(59)+(9)=68 (括号分别指在十、百为出现的次数)

所以,在计算个位的时候其实就等于这个数的个位前面位数的数。

然后我们还可以发现在十位中,也是十位前面位数的数*十位后面数的位:10^1.

然后百位的计算也是一样的,因为前面没数了,所以后面直接乘以一个10^2.

计算1所计算的是:以当前要求的数码k不变,其位置i前后数位所能为其组成的方案数。

计算2:

例如计算以‘1’为当前位置i的开头的方案数——什么意思呢?就是说例如当前

999,位置i=2——也就是以1开头的十位数,i=3——以1开头的百位数,i=4——以1开头的千位数,i=1——以1开头的个位数。

所以像上面的两个例子:

999,在算个位的时候,其实是有三步的,第三步就是以1开头方案数——只有1次

  在算十位的时候,其实是有两步的,第二步就是以1开头的方案数——有10次

          在算百位的时候,其实只有一步的,第一步就是以1开头的方案数——有100次

还有栗子:

184,计算个位的时候,以1开头的方案数——1次

  计算十位的时候,以1开头的方案数——10次

  计算百位的时候,以1开头的方案数——84次

由此我们可得出结论:当以数码k,在第i位的时候,只要数码k>s[length(s)-i+1],就可以直接加10^(i-1),否则,如果k=s[length(s)-i+1]的话,就加上其后面的数所组成的数。再如果——小于的话——则不用计算。



还是贴一下程序来理解吧:

function qiu(k:Longint):int64;//k代表的是要求的数码var        i:Longint;begin        qiu:=0;        for i:=1 to length(s) do        begin                inc(qiu,(n div a[i+1])*a[i]);//这里就是算i其前后位数的方案数。                if ord(s[length(s)-i+1])-48>k then inc(qiu,a[i]); //这里就是算如果以当前位为开头,要求的这一位是大于k的,则没有影响,直接+a[i]就行,这里就好似上面举的两个例子中计算个、十、百位的其中一步。                if ord(s[length(s)-i+1])-48=k then inc(qiu,n mod a[i]+1); //这里就是上面说的如果是大于k的,直接加a[i],但如果是=k的,则加后面数的大小,例如158,则加58.如果是小于k的则没必要计算了。        end;end;

a[i]表示i^10,qiu记录总和,k表示要求的数字编号,s表示要求的数。


这个方法只是计算1~9的,如果说要计算0的话,则在结果后面还要减去一个11111...

for i:=1 to length(s) do                dec(ans,a[i]);

1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 孕妇酮体4个加号怎么办 来月经量少发黑怎么办 月经量少且发黑怎么办 验孕试纸不加深怎么办 刚怀孕染了头发怎么办 2个月宝宝不吃奶怎么办 孕妇尿隐血1十是怎么办 尿葡萄糖2个加怎么办 孕妇尿葡萄糖2加怎么办 尿的蛋白高怎么办治疗 孕妇尿蛋白高是怎么办 孕妇尿细菌数高怎么办 孕妇尿细菌很高怎么办 尿微量总蛋白高怎么办 衣服上碳素笔油怎么办 卡油泵总成坏了怎么办 汽车燃油泵坏了怎么办 新车汽油泵坏了怎么办 墓地的树枯死了怎么办 大车尿素不烧了怎么办 不烧尿素限扭怎么办 汽车首保过期了怎么办 假体隆鼻后透光怎么办 熊猫血有抗体了怎么办 rh阴性血怀二胎怎么办 熊猫血怀二胎了怎么办 rh阴性血要二胎怎么办 全血粘度都偏高怎么办 血脂高血粘度高怎么办 全血粘度值1偏高怎么办 全血粘度3偏高怎么办 血粘度高的症状怎么办 粉瘤感染化脓了怎么办 乌药剂量用大了怎么办 水卡消磁了怎么办妙招 电卡消磁了怎么办妙招 入园磁卡消磁了怎么办 透析中静脉压高怎么办 腰间盘突出压迫神经腿疼怎么办 肺热引起的发烧怎么办 肺热引起的痘痘怎么办