题目描述:从1页到n页,统计0到9出现的次数
题解一:暴力
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main(){
int c[10]={0};
int n;
cin>>n;
for(int i=1;i<=n;i++){
int tmp=i;
while(tmp){
c[tmp%10]++;
tmp/=10;
}
}
for(int i=0;i<=9;i++){
cout<<c[i]<<endl;
}
}
题解二:递归
用递归方法显然比暴力节省了时间,解题思路可分为以下6步。
步骤一:求数n的位数len
公式log(n)+1;
可用cmath里的函数:double log10(double x)来求解
步骤二:划分区间并加和
把数n划分成区间,eg,
223,可划分成00-99,100到199(两个区间)
3333,可划分为000-999,1000到1999,2000到2999(三个区间)
在这每一个区间内(除了最高位)0-9出现的次数是(len-1)*10^(len-2)----(列举一下可发现规律)
现在我们知道了每个区间的0-9出现的次数,该求区间数了:
如233区间数就是2,3333的区间数就是3,所以数n的区间数p是n/(10^(len-1))
综上,如3333,从000-2999(除了最高位)0到9的每个数的个数为p*(len-1)*10^(len-2)
步骤三:最高位加和
如3333,上一步我们已经算好从000到2999,(除了最高位)0到9每个数的和了
现在来算最高位
最高位(第4位)的数有0、1、2、3
0:0000-0999共1000个
1:1000-1999共1000个
2:2000-2999共1000个
3:3000-3333共333+1个
设最高位为m可得代码如下
for(int i=0;i<m;i++){
c[i]+=pow(10.0,len);
}
c[m]+=1+n%((int)pow(10.0,len-1));
步骤四:递归
处理完3333的000-2999个数以及3作为第len位的情况剩下333递归处理333
即要处理t=n%(10^(len-1))
注意:
(1)若t为0,比如200,此时c[0]要加len-1也就是2!
若数10,则t为0,c[0]要加len-1,也就是1,
然后结束递归.
(2)若t不为0,这里特判一下,若lenT!=len-1说明是诸如10010这种情况,中间两个0还是要处理的,
c[0]+=(len-lenT-1)*(t+1)
步骤五:递归结束后处理0的情况
for(int i=0;i< len;i++) {
c[0]-=(int)pow(10.0,(i));
}
这个公式可以这么理解:
当n=21时,要减去的0有:10^0(红色部分)+10^1(黄色部分)
当n=123时,要减去的0有:10^0(红色部分)+10^1(黄色部分)+10^2(灰色部分)
步骤六:输出
(ps,做的时候遇到了一个坑,pow(a,b)函数因为精度问题,有时会输出不准确的结果,所以,在前面加一个round函数,就准确了……我算的时候pow(10.0,len)len是2结果算出来是99……,这个问题还是需要注意的!)
代码如下:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int c[10];
void solve(int n){
//数n的位数
int len=log10(n)+1;
//最高位的值
int p=n/((int)round(pow(10.0,len-1)));
for(int i=0;i<10;i++){
c[i]+=p*(len-1)*(int)round(pow(10.0,len-2));
}
for(int i=0;i<p;i++){
c[i]+=(int)round(pow(10.0,len-1));
}
int t=(int)round(pow(10.0,len-1));
t=n%t;
if(t==0){//如果t为0
c[p]++;//最高位加1
c[0]+=len-1;//0位加len-1
return ;
}
int lenT=log10(t)+1;
if(lenT!=len-1){//若像10010这种情况,中间2个0也要相应的处理
c[0]+=(len-lenT-1)*(t+1);
}
c[p]+=1+t;
return solve(t);
}
int main(){
int n;
while(cin>>n){
fill(c,c+10,0);
int len=log10(n)+1;
solve(n);
for(int i=0;i<len;i++){
c[0]-=(int)round(pow(10.0,i));
}
for(int i=0;i<10;i++){
cout<<i<<" : "<<c[i]<<endl;
}
}
}