統計數字問題(10^9的大數)
問題描述:一本書的頁碼從自然數1 開始順序編碼直到自然數n。書的頁碼按照通常的習慣編排, 每個頁碼都不含多餘的前導數字0。例如,第6 頁用數字6 表示,而不是06 或006 等。數 字計數問題要求對給定書的總頁碼n,計算出書的全部頁碼中分別用到多少次數字0,1, 2,…,9。
資料輸入:每個檔案只有一行,給出表示書的總頁碼的整數n(1<=)
資料輸出:輸出檔案共10行,在第k(k=1,2,3...10)行輸出頁碼中用到數字k-1的次數
輸入檔案示例:11
輸出檔案示例:1 4 1 1 1 1 1 1 1 1
問題分析:
拿到題第一感覺是直接從1迴圈到n,然後計算每一位的數字計算總數
for(int i=1;i<=n;i++){ while(i/10){ total[i%10]++; i/=10; } }
可以看到,隨著n的增大,程式的時間複雜度也在增大,所以方法不可取
然後我就用老師上課講的方法試說一下吧
演算法核心思想是考慮數字k在每個位上出現的次數然後加和
然後現在來找規律
拿9527舉例吧,現在十位上是2,假設考慮十位上的數比2大,例如3,則十位上出現3的數字為
30-39 130-139....9430-9439 共(94+1)*10個
假設考慮十位上的數比2小,例如1,則十位上出現數字2的數字為
10-19 110-119....9510-9519 共(95+1)*10個
假設考慮十位上的數等於2,則十位上出現2的數字為
20-29 120-129....9420-9429 9520-9527 共(94+1)*10+(個位+1)
再用百位上的5舉例,現在百位上是5,假設考慮百位上的數比5大,例如6,則百位上出現6的數字為
600-699 1600-1699....8600-8699 共(8+1)*100個
假設考慮百位上的數比5小,例如3,則百位上出現數字3的數字為
300-399 1300-1399....9300-9399 共(9+1)*100個
假設考慮百位上的數等於5,則百位上出現5的數字為
500-599 1500-1599....8500-8599 9500-9527 共(8+1)*100+(後兩位+1)
然後假設當前位為cur,該位之前的數字為high,該位之後的數字為low,cur所在的位的權重為weight(十位為10,百位為100),則可以總結出的規律為:
k < cur時,k在該位出現的次數為(high+1)*weight
k > cur時,k在該位出現的次數為 high*weight
k = cur時,k在該位出現的次數為 high*weight+low+1
Note:
因為0前必須有數字,所以需要單獨考慮0
看程式碼吧還是
#include<iostream>
#include<cstring>
using namespace std;
__int64 total[10];//記錄每個數出現的個數
int main(){
int n;
__int64 low,cur,high;
int weight=1;//權值
cout<<"Enter the total page number n:";
cin>>n;
memset(total,0,sizeof(total));
while(n/weight){//將0單獨考慮
low=n%weight;//當前位之後的數字
cur=(n/weight)%10;//當前位
high=n/(weight*10);//當前位之前的數字
if(!high){
weight*=10;
continue;
}
else if(cur==0){
total[0]+=((high-1)*weight+low+1);
}
else{
total[0]+=high*weight;
}
weight*=10;
}
weight=1;
while(n/weight){//1~9直接按照規律
low=n%weight;//當前位之後的數字
cur=(n/weight)%10;//當前位
high=n/(weight*10);//當前位之前的數字
for(int i=1;i<=9;i++){
if(i<cur){
total[i]+=(high+1)*weight;
}
else if(i>cur){
total[i]+=(high*weight);
}
else if(i==cur){
total[i]+=(high*weight+low+1);
}
}
weight*=10;
}
for(int i=0;i<=9;i++){
cout<<total[i]<<endl;
}
return 0;
}