ACM 统计数字问题
2015-03-17 13:29
323 查看
NOJ 1201
题目描述
一本书的页码从自然数1 开始顺序编码直到自然数n。书的页码按照通常的习惯编排,
每个页码都不含多余的前导数字0。例如,第6 页用数字6 表示,而不是06 或006 等。数字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1,2,…,9。
给定表示书的总页码的10 进制整数n (1≤n≤10^9) 。编程计算书的全部页码中分别用到多少次数字0,1,2,…,9。
输入
输入只有1 行,给出表示书的总页码的整数n。
输出
输出共有10行,在第k行输出页码中用到数字k-1 的次数,k=1,2,…,10。
样例输入
11
样例输出
1
4
1
1
1
1
1
1
1
1
题目中提到,不得出现前导0。可是,我向来不是一个听话的人,我就要出现前导0,大不了后面再减去罢了。
题目中提到,从1开始计数,可是,我向来不是一个听话的人,我就要从0开始计数,大不了后面再减去最前面的0罢了。
(做ACM一定要任性)
我们提前做两件事情,一是计算你输入的数字的长度,第二个是完成乘方的运算。
计算数字的长度很简单,不停地除以10即可。
而乘方运算,我们简单相乘即可。当然我这里为了显摆一下,采用O(logn)的快速乘方法(二元法)。
下面我们来看最主要的部分。我们假设总页数是1234。这个时候我们的页码分别是0000、0001、0002、。。。1234。
我们先看看0~9在个位分别出现了多少次吧。
显然:
0 124次
1 124次
2 124次
3 124次
4 124次
5 123次
6 123次
7 123次
8 123次
9 123次
看出规律没有?没有?再看看0~9在十位出现的次数。
0 13*10次
1 13*10次
2 13*10次
3 12*10+5次
4 12*10次
5 12*10次
6 12*10次
7 12*10次
8 12*10次
9 12*10次
假设在这个过程中,k=3(即十位数字)。则对于j=0~9,有j出现的次数为:
如果j小于k,(左半部分的数字+1)*10^右半部分的位数
如果j等于k,左半部分的数字*10^右半部分的位数+右半部分的数字+1
如果j大于k,左半部分的数字*10^右半部分的位数
看来这下子我们需要一个函数来提取一个整数左半部分和右半部分的数字了。
我们假设如下标记
数字 1 2 3 4
index 3 2 1 0
设计函数int subNumber(int page,int start,int end),例如subNumber(1,1)=3,subNumber(0,2)=234,subNumber(3,2)=0。
在i=1的时候(十位),k=subNumber(page,i,i)(即3),假设每个数字出现次数为inc,则
令i=0~len-1即可。
最后,我们需要为自己的前导0和从0计数付出代价。
假设是四位数吧,有多少前导0呢?
三位数的前导0,即100~999,每个数字1个,即900*1
二位数的前导0,即10~99,每个数字2个,即90*2
一位数的前导0,即1~9,每个数字3个,即9*3
假如是长度为len位数的数字呢?
result[0]-=10^(len-i-2)*9*(i+1),i从0~len-1即可。
最后一个,0000,即result[0]-=len。
最后,把代码整合一下就大功告成啦~
题目描述
一本书的页码从自然数1 开始顺序编码直到自然数n。书的页码按照通常的习惯编排,
每个页码都不含多余的前导数字0。例如,第6 页用数字6 表示,而不是06 或006 等。数字计数问题要求对给定书的总页码n,计算出书的全部页码中分别用到多少次数字0,1,2,…,9。
给定表示书的总页码的10 进制整数n (1≤n≤10^9) 。编程计算书的全部页码中分别用到多少次数字0,1,2,…,9。
输入
输入只有1 行,给出表示书的总页码的整数n。
输出
输出共有10行,在第k行输出页码中用到数字k-1 的次数,k=1,2,…,10。
样例输入
11
样例输出
1
4
1
1
1
1
1
1
1
1
题目中提到,不得出现前导0。可是,我向来不是一个听话的人,我就要出现前导0,大不了后面再减去罢了。
题目中提到,从1开始计数,可是,我向来不是一个听话的人,我就要从0开始计数,大不了后面再减去最前面的0罢了。
(做ACM一定要任性)
我们提前做两件事情,一是计算你输入的数字的长度,第二个是完成乘方的运算。
计算数字的长度很简单,不停地除以10即可。
int mylength(int page) { int s=0; while(page>0) { page/=10; s++; } return s; }
而乘方运算,我们简单相乘即可。当然我这里为了显摆一下,采用O(logn)的快速乘方法(二元法)。
int mypow(int x,int y) { int s=1,q=x; while(y>0) { if(y%2) { s=s*q; } q=q*q; y=y/2; } return s; }
下面我们来看最主要的部分。我们假设总页数是1234。这个时候我们的页码分别是0000、0001、0002、。。。1234。
我们先看看0~9在个位分别出现了多少次吧。
显然:
0 124次
1 124次
2 124次
3 124次
4 124次
5 123次
6 123次
7 123次
8 123次
9 123次
看出规律没有?没有?再看看0~9在十位出现的次数。
0 13*10次
1 13*10次
2 13*10次
3 12*10+5次
4 12*10次
5 12*10次
6 12*10次
7 12*10次
8 12*10次
9 12*10次
假设在这个过程中,k=3(即十位数字)。则对于j=0~9,有j出现的次数为:
如果j小于k,(左半部分的数字+1)*10^右半部分的位数
如果j等于k,左半部分的数字*10^右半部分的位数+右半部分的数字+1
如果j大于k,左半部分的数字*10^右半部分的位数
看来这下子我们需要一个函数来提取一个整数左半部分和右半部分的数字了。
我们假设如下标记
数字 1 2 3 4
index 3 2 1 0
设计函数int subNumber(int page,int start,int end),例如subNumber(1,1)=3,subNumber(0,2)=234,subNumber(3,2)=0。
int subNumber(int page,int start,int end) { int len; if(start>end) return 0; return page/mypow(10,start)%mypow(10,end-start+1); }
在i=1的时候(十位),k=subNumber(page,i,i)(即3),假设每个数字出现次数为inc,则
if(j<k) inc=(subNumber(page,i+1,len-1)+1)*mypow(10,i); else if(j==k) inc=(subNumber(page,i+1,len-1))*mypow(10,i)+(subNumber(page,0,i-1)+1); else inc=(subNumber(page,i+1,len-1))*mypow(10,i);
令i=0~len-1即可。
最后,我们需要为自己的前导0和从0计数付出代价。
假设是四位数吧,有多少前导0呢?
三位数的前导0,即100~999,每个数字1个,即900*1
二位数的前导0,即10~99,每个数字2个,即90*2
一位数的前导0,即1~9,每个数字3个,即9*3
假如是长度为len位数的数字呢?
result[0]-=10^(len-i-2)*9*(i+1),i从0~len-1即可。
最后一个,0000,即result[0]-=len。
最后,把代码整合一下就大功告成啦~
#include<stdio.h>
#include<math.h>
#include<string.h>
#define MIN(x,y) ((x)<(y)?(x):(y))
#define MAX(x,y) ((x)>(y)?(x):(y))
#define ABS(x) (((x)>0)?(x):(-(x)))
#define bool int
#define true 1
#define false 0
#define TRUE 1
#define FALSE 0
#define int64 long long
#define MAXLEN 1000005
int mypow(int x,int y) { int s=1,q=x; while(y>0) { if(y%2) { s=s*q; } q=q*q; y=y/2; } return s; }
int mylength(int page) { int s=0; while(page>0) { page/=10; s++; } return s; }
int subNumber(int page,int start,int end) { int len; if(start>end) return 0; return page/mypow(10,start)%mypow(10,end-start+1); }
int main()
{
int page=105;
int64 result[10]={0};
int i,j,k,len=mylength(page);
int inc;
scanf("%d",&page);
len=mylength(page);
for(i=0;i<len;i++)
{
k=subNumber(page,i,i);
for(j=0;j<10;j++)
{
if(j<k) inc=(subNumber(page,i+1,len-1)+1)*mypow(10,i);
else if(j==k) inc=(subNumber(page,i+1,len-1))*mypow(10,i)+(subNumber(page,0,i-1)+1);
else inc=(subNumber(page,i+1,len-1))*mypow(10,i);
result[j]+=inc;
}
}
for(i=0;i<len-1;i++)
{
result[0]-=mypow(10,len-i-2)*9*(i+1);
}
result[0]-=len;
for(i=0;i<10;i++)
{
printf("%I64d\n",result[i]);
}
}