PAT 1049 Counting Ones (30分) 程式設計之美--1的個數
題目
The task is simple: given any positive integer N, you are supposed to count the total number of 1's in the decimal form of the integers from 1 to N. For example, given N being 12, there are five 1's in 1, 10, 11, and 12.
Input Specification:
Each input file contains one test case which gives the positive N (≤230
Output Specification:
For each test case, print the number of 1's in one line.
Sample Input:
12
Sample Output:
5
解析
題意:
給定一個正整數N
,求從1-N
的所有正整數中,1
總共出現了幾次。
思路:
這道題是《程式設計之美》上面的一道題(第2.4節),需要通過分析來總結規律,然後總結出公式,如果暴力去“數”1的個數,顯然會超時。但如果通過公式來算的話,時間複雜度就直接降到O(1)。具體分析過程比較複雜,詳情參見《程式設計之美》2.4節“1的數目”,這裡只給出結論——從右往左拆解當前數字,逐位分析每個位置出現1的次數,然後統計其規律與當前位置左右部分數字的關係,最後累加,即為結果。
以數字N=345、N=305、N=315
十位
上是1
的數字個數。將數字分成3
部分:百位、十位、個位
。
- 當
N=345
時,從1-345
這345
個數中,百位
數字可以出現0、1、2、3
四種,每種百位
數字都可以跟一個數字為1
的十位
,而每種十位
數字可以跟0-9
這十種數字,所以從1~345
這345
個數中,十位
數字為1
的數共有(3+1)×10=40
個,故十位
上的1
共出現40
次。 - 當
N=305
時,百位
上數字依然可以出現0、1、2、3
四種,但要注意,百位
數字為3
時,後面不能再跟數字為1
的十位
,因為這樣的數字已經大於305
了,所以從1~305
這305
個數中,十位
數字為1
的數共有3×10=30
個,故十位
上的1
共出現30
次。 - 當
N=315
時,百位
上數字依然可以出現0、1、2、3
百位
數字為3
時,後面可以再跟數字為1
的十位
,但這樣的數字個位
上只能出現0-5
這6
個數,即310、311、312、313、314、315
,其他數字都會大於315
,所以從1~315
這315
個數中,十位
數字為1
的數共有3×10+(5+1)=36
個,故十位
上的1
共出現36
次。
綜上,對於任意一個數字N
,當要判斷從右向左
數第i
位上1
出現的次數num
時,可以將這個數字分成三部分,分別用left、current、right
表示,即left=數字N在i位左側的數字、current=數字N在第i位的數字、right=數字N在i位右側的數字
。例如數字N=123456
,判斷從右向左第3位
也就是百位
上,即數字4
所在位置1
出現的次數時,left=123、current=4、right=56
。此時分三種情況進行計算:
-
current=0:num = left × 10i
-
current=1:num = left × 10i+ (right + 1)
-
current>1:num = (left + 1) × 10i
-
其中
10^i^
就表示的是當前處理的是個位、十位、還是百位、千位.......
所以如果使用result
儲存最後的答案,用a
表示當前是個、十、百、千位……使用left
表示左邊部分表示的數字,right
表示右邊部分表示的數字。
則從右往左
(從個位往高位)開始遍歷,判斷當前位置的字元:
-
若是
0
:則在當前位置可以出現1的次數為left * a
次。 -
若是
1
:則在當前位置可以出現1的次數為left * a + right + 1
次 -
其他
(2-9):則在當前位置出現1 的次數為(left+1)*a
程式碼
#include <iostream>
using namespace std;
int main() {
int n, ans = 0, radix = 1, left, right, curr;
cin >> n;
while (n / radix) {
left = n / (radix * 10); curr = n / radix % 10; right = n % radix;
if (curr == 0) ans += left * radix;
else if (curr == 1) ans += left * radix + right + 1;
else ans += (left + 1) * radix;
radix *= 10;
}
cout << ans;
return 0;
}