PAT-ADVANCED1049——Counting Ones
題目描述:
題目翻譯:
1049 計算1的個數
任務很簡單:給定任何正整數N,你應該計算從1到N的整數的十進位制形式的1的總數。例如,給定N為12,在1, 10, 11, 12中有5個1。
輸入格式:
每個輸入檔案包含用一個測試用例。該測試用例給出了一個正整數N(<= 2 ^ 30)。
輸出格式:
對每個測試用例,在一行中輸出1的個數。
輸入樣例:
12
輸出樣例:
5
知識點:數學
思路:統計每一位在1 ~ N的過程中可能出現1的次數
如果通過從1數到n一個個列舉來算1的個數,測試點4和測試點6會超時。
這個思路很巧妙,是分別統計每一位在1 ~ N的過程中可能出現的次數,再累加即得1的個數總和
(1)以字串的形式接收資料,遍歷該字串的每一位。
(2)對於字串的首位(首位不可能是0),首位的左邊沒有數字,首位右邊的數字是right。
a:如果首位是1,為了不超過N,對於右邊的數字我們可以取0 ~ right,總共有right + 1種情況。
b:如果首位不是1,對於30710來說,1後面的數字可以取0 ~ 9999,共10000種情況。
(3)對於字串的末位,末位的右邊沒有數字,末位左邊的數字是left。
a:如果末位是0,則其左邊可以取的範圍是0 ~ left - 1,共left種情況,對於30710來說,末位0前面可以取0 ~ 3070。
b:如果末位不是0,則其左邊可以取的範圍是0 ~ left,共left + 1種情況。
(4)對於既不是字串首位也不是字串末位的情況,其左邊的數字是left,右邊的數字是right。
a:如果當前位的是0,則其左邊可以取的範圍是0 ~ left - 1,對於30710的中間的0來說,右邊可以取的範圍是0 ~ 999。共left * 1000種情況。
b:如果當前位的是1,對於30710的中間的1來說,當左邊取0 ~ 306時,右邊可以取0 ~ 9;當左邊取307時,右邊只能取0。
c:如果當前位既不是0也不是1,對於30710的中間的7來說,左邊可以取0 ~ 30,右邊可以取0 ~ 99。
時間複雜度是O(n ^ 2),其中n為N的位數。空間複雜度是O(1)。
C++程式碼:
#include<iostream> #include<string> #include<cmath> using namespace std; int main() { string input; cin >> input; int result = 0; for(int i = 0; i < input.length(); i++) { if(i == 0) { if(input[i] == '1') { int right = 0; for(int j = 1; j < input.length(); j++) { right = right * 10 + input[j] - '0'; } result += right + 1; } else { result += (int)pow(10, input.length() - 1); } } else if(i == input.length() - 1) { int left = 0; for(int j = 0; j < input.length() - 1; j++) { left = left * 10 + input[j] - '0'; } result += left + 1; if(input[i] == '0') { result--; } } else { int left = 0, right = 0; for(int j = 0; j < i; j++){ left = left * 10 + input[j] - '0'; } for(int j = i + 1; j < input.length(); j++){ right = right * 10 + input[j] - '0'; } if(input[i] == '0'){ result += left * (int)pow(10, input.length() - i - 1); }else if(input[i] == '1'){ result += left * (int)pow(10, input.length() - i - 1) + right + 1; }else{ result += (left + 1) * (int)pow(10, input.length() - i - 1); } } } printf("%d\n", result); return 0; }
C++解題報告: