题目
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(\le2^{30}) N(≤230).
Output Specification:
For each test case, print the number of 1’s in one line.
Sample Input:
12
Sample Output:
5
题目大意
给一个数,计算所有小于等于这个数的数字中的1的个数和。
思路
这既是一道找规律的题,也是一道数学问题,对于这个数,我们可以逐个逐个数的分析,对于每个数可分为三种情况:
- 当前数字是0;例如数字2202,十位数字为0,那么十位出现1的次数由左边的数22决定,即十位出现1的次数是 22 × 10 × 1 22\times10\times1 22×10×1.(2110、2010、1910…,这是乘22的原因,同时每个区段又有2110、2111、2112…2119,这是乘10的原因)
- 当前数字是1;例如数字2212,十位数字为1,那么十位出现1的次数由三部分组成,左边的22,右边的2以及本身,十位出现1的次数是 22 × 10 × 1 + 2 + 1 22\times10\times1 + 2 + 1 22×10×1+2+1.(2+1是因为右边可以组成2210、2211、2212)
- 当前数字大于1;例如数字2222,十位数字为2,那么十位出现1的次数由两部分组成,左边的数和右边的数 22 × 10 × 1 + 10 × 1 22\times10\times1 + 10\times1 22×10×1+10×1.(加上 10 × 1 10\times1 10×1是因为有2210、2211…2219)
按照这样的规律就可得到如下代码:
代码
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int main(){
int n;
scanf("%d", &n);
int left, now, right, cnt = 1, ans = 0;
while(n/cnt){
left = n/(cnt*10), now = n/cnt%10, right = n%cnt;
if(now == 0)
ans += left*cnt;
else if(now == 1)
ans += left*cnt + right + 1;
else
ans += (left + 1)*cnt;
cnt *= 10;
}
printf("%d\n", ans);
return 0;
}