Problem Description:
The task is simple: given any positive integer N N N, you are supposed to count the total number of 1’s in the decimal form of the integers from 1 to N N N. For example, given N N 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 ( ≤ 2 30 ) . N (\leq 2 ^{30} ). N(≤230).
Output Specification:
For each test case, print the number of 1’s in one line.
Sample Input:
12
Sample Output:
5
Problem Analysis:
假设当前要讨论的数是 abcdefg \text{abcdefg} abcdefg,我们当前对 d \text{d} d 这一位的取值进行讨论,看在 d d d 的不同取值下,这一位是1的数有多少个。
- 若 d = 0 \text{d} = 0 d=0,则前三位的取值范围为 000 ∼ abc − 1 000\sim \text{abc} - 1 000∼abc−1,后三位的取值范围为 000 ∼ 999 000\sim 999 000∼999,这样合法的数就有 abc ⋅ 1000 \text{abc}\cdot 1000 abc⋅1000 个
- 若
d
=
1
\text{d} = 1
d=1
- 前三位取 000 ∼ abc − 1 000\sim \text{abc} - 1 000∼abc−1,后三位取 000 ∼ 999 000\sim 999 000∼999,这样合法的数就有 abc ⋅ 1000 \text{abc}\cdot 1000 abc⋅1000 个
- 前三位取 abc \text{abc} abc,后三位取 000 ∼ efg 000\sim \text{efg} 000∼efg,这样合法的数就有 efg + 1 \text{efg} + 1 efg+1 个
- 若 d > 1 d > 1 d>1,则前三位取 000 ∼ abc 000\sim \text{abc} 000∼abc,后三位取 000 ∼ 999 000\sim 999 000∼999,这样合法的数有 ( abc + 1 ) ⋅ 1000 (\text{abc} + 1)\cdot 1000 (abc+1)⋅1000
每一位都像这样讨论,就可以算出来所有合法的数的个数。
Code
Version 1
循环迭代
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int dp(int n)
{
vector<int> nums;
while (n) nums.push_back(n % 10), n /= 10;
int res = 0;
for (int i = nums.size() - 1; i >= 0; i -- )
{
int x = nums[i];
int left = 0, right = 0, power = 1;
for (int j = nums.size(); j > i; j -- ) left = left * 10 + nums[j];
for (int j = i - 1; j >= 0; j -- ) right = right * 10 + nums[j], power *= 10;
if (x == 0) res += left* power;
else if (x == 1) res += left * power + right + 1;
else res += (left + 1) * power;
}
return res;
}
int main()
{
int n;
cin >> n;
cout << dp(n) << endl;
return 0;
}
Version 2
记忆化搜索
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 35;
int dp[N][N]; // dp[i][j] 表示当前枚举到第i位数时,已经统计了有j个1,此时出现的1的个数
int a[N];
int dfs(int pos, int sum, int limit)
{
if (!pos) return sum; // 枚举完了,返回sum
if (!limit && dp[pos][sum] != -1) return dp[pos][sum];
int res = 0, up = limit ? a[pos] : 9;
for (int i = 0; i <= up; i ++ )
{
int t = sum;
if (i == 1) t = sum + 1;
res += dfs(pos - 1, t, limit && i == up);
}
if (!limit) dp[pos][sum] = res;
return res;
}
int calc(int x)
{
memset(dp, -1, sizeof dp);
int pos = 0;
while (x) a[ ++ pos] = x % 10, x /= 10;
return dfs(pos, 0, 1);
}
int main()
{
int n;
cin >> n;
printf("%d\n", calc(n));
return 0;
}