您的位置:首页 > 其它

统计页码数字问题

2013-06-10 12:29 246 查看
统计数字问题

问题描述:
一本书的页码从自然数1 开始顺序编码直到自然数n。书的页码按照通常的习惯编排,每个页码都不含多余的前导数字0。例如,第6
页用数字6 表示,而不是06 或006 等。数
字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1,2,…,9。

编程任务:

给定表示书的总页码的10 进制整数n (1≤n≤109)
。编程计算书的全部页码中分别用到多少次数字0,1,2,…,9。

数据输入:

输入数据由文件名为input.txt的文本文件提供。每个文件只有1 行,给出表示书的总页码的整数n。

结果输出:

程序运行结束时,将计算结果输出到文件output.txt中。输出文件共有10行,在第k行
输出页码中用到数字k-1 的次数,k=1,2,…,10。

输入文件示例 输出文件示例
input.txt
11

output.txt
1
4
1
1
1
1
1
1
1
1

解题思路1:根据输入的数字Num,从1到Num 一 一统计每个数字出现的次数。

算法时间复杂度:O(n*log10(n))

算法:

for(i = 1; i <= n; i++)

{

t = i;

while(t) {

count[t%10]++;
//count数组是每个数字出现次数的计数器

t/=10;

}

}





f(n)=10*f(n-1)+10^(n-1)
f(n-1)=10*f(n-2)+10^(n-2)
f(n-2)=10*f(n-3)+10^(n-3)

.

.

.
f(2)=10f(1)+10^1

可见:f(n)=10^(n-1)+(n-1)*10^(n-1)

即:f(n)=n*10^(n-1),利用此公式计算每个数字的出现次数,计算0的出现次数时,最后减去多余的前导0的出现次数即可。

举例(取区间法):

比如计算32871,从高位向低位看,可以看到该数包含3个0000~9999的区间(0~9999,10000~19999,20000~29999),即每个数字出现的次数为3f(4),其中多余的前导0出现在0000~9999区间中,经观察可知多了10^3+10^2+10^1+1个0。(0000-0999,10^3个0;000-099,10^2个0;00-09,10^1个0;开始的0,10^0个0)

其中,最高位上1,2共出现10^4次。接着计算30000~32871,此处最高位的3共出现2872次。于是,着眼点就落在0000~2871中各数字的出现次数上,此时依照上面的取区间法,一次一次去掉最高位,直到剩下个位时为止,这里不会有多余的前导0(你懂的)。

算法时间复杂度:log10(n)

inline int Process(int n)
//f(n)=n*10^(n-1)原著公式

{

return n*pow(10,n-1);

}

void Dispose(int a[],int n,int &c0)//页码处理过程

{

int t=log10(n),temp=n;//假设输入数字为m位,t表示m-1

while(t)

{

temp/=pow(10,t);//temp为最高位数字

if(temp!=0)

{

n-=temp*pow(10,t);//去除最高位数字

for(int i=0;i<=9;i++)

a[i]+=temp*Process(t);//每位数字加上m*f(m-1),即一个高位乘以低m-1位的区间

}

a[temp--]+=n+1;//高位数字因低m-1位数字的出现次数

while(temp>=0)a[temp--]+=pow(10,t);//同上

c0+=pow(10,t);//需要去除多余0的个数

//while与c0这两步可改为while(temp>0)a[temp--]+=pow(10,t);这样做在主函数中只需减去1个多余前导0

if(n<<span
style="color: rgb(0, 0, 255);
">10&&t==1)

{

for(int i=0;i<=n;i++)

a[i]++;

n=0;

}

temp=n;

t--;

}

}

源代码:

#include

#include

#include

#include

#include

using namespace std;

inline int Process(int n)//f(n)=n*10^(n-1)原著公式

{

return n*pow(10,n-1);

}

void Dispose(int a[],int n,int &c0)//页码处理过程

{

int t=log10(n),temp=n;//假设输入数字为m位,t表示m-1

while(t)

{

temp/=pow(10,t);//temp为最高位数字

if(temp!=0)

{

n-=temp*pow(10,t);//去除最高位数字

for(int i=0;i<=9;i++)

a[i]+=temp*Process(t);//每位数字加上m*f(m-1),即一个高位乘以低m-1位的区间

}

a[temp--]+=n+1;//高位数字因低m-1位数字的出现次数

while(temp>=0)a[temp--]+=pow(10,t);//同上

c0+=pow(10,t);//需要去除多余0的个数

//while与c0这两步可改为while(temp>0)a[temp--]+=pow(10,t);这样做在主函数中只需减去1个多余前导0

if(n<<span
style="color: rgb(0, 0, 255);
">10&&t==1)

{

for(int i=0;i<=n;i++)

a[i]++;

n=0;

}

temp=n;

t--;

}

}

void write()

{

fstream t;

t.open("F:\\Input.txt",ios::out);

if(!t)

{

cout<<"Can't open
Input.txt"<<endl;

return;

}

srand((unsigned)time(NULL));

for(int i=0;i<<span
style="color: rgb(0, 0, 255); ">20;i++)

t<<rand()%10000<<endl;

t.close();

}

void main()

{

int n,a[10]={0},c0=0;//c0是多余的0的总数

fstream f,fo;

write();

fo.open("F:\\Output.txt",ios::out);

if(!fo)

{

cout<<"Can't open
Output.txt"<<endl;

return;

}

f.open("F:\\Input.txt",ios::in);

if(!f)

{

cout<<"Can't open
Input.txt"<<endl;

return;

}

while(!(f>>n).eof())

{

if(n<<span
style="color: rgb(0, 0, 255); ">10)

{

for(int i=1;i<=n;i++)

a[i]++;

for(int i=0;i<=n;i++)

cout<<a[i]<<endl;

return;

}

Dispose(a,n,c0);

a[0]-=c0+1;//减去多余的0

for(int i=0;i<=9;i++)

cout<<a[i]<<"
";

cout<<endl;

for(int i=0;i<<span
style="color: rgb(0, 0, 255); ">10;i++)

fo<<a[i]<<"
";

fo<<endl;

for(int i=0;i<<span
style="color: rgb(0, 0, 255); ">10;i++)

a[i]=0;

c0=0;

}

f.close();

fo.close();

}

源码在Vs2012中编写,在其他编译其中可能会存在兼容问题(某些代码格式的不支持),请自行调试(很简单)。

运行前须在F盘根目录下分别建立“Input.txt”与“Output.txt”两个文本文件,也可以将文件处理部分注释掉运行。

算法1,2可自行设计CLOCK函数测试时间,算法2会明显快于算法1。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: