原题:https://pintia.cn/problem-sets/994805342720868352/problems/994805430595731456
题目大意
一道有意思的数学题,给出一个数字n,给出所有小于等于这个数的数字中1的个数和。
题目分析
判断所有
[
1
,
n
]
[1,n]
[1,n]的数字中1的个数,我们可以每位单独判断,计算每一位为1时有多少种情况,然后对所有情况进行求和。每一位为1有三种情况,假设当前在a分位
,a为10,100,1000依次递推。
- 若当前位置数字为 0(小于1),则如果该位为1,则其高位
left
就应该变小,即[0,left-1]
种情况,每种情况时,其低位right
都可以任意取值。例如对于数字21205
来说,判断其十位为0,此时高位可以是[0,211]
,每一种情况,其低位都可以取值[0,9]
。所以总的情况是left * a
种 - 若当前位置数字为1,则除去该位置,剩余位置的数字,我们只需要小于等于就好了,一共有
left * a + right + 1
种情况,也就是在第一种情况的基础下,加上高位不变的情况。例如对于数字21215
的十位数字,我们一共有[0,2125]
种情况。 - 若当前位置数字大于1,则我们将改为变成1后,高位可以取
[0,left]
,每种情况低位可以取值[0,a]
,一共(left + 1) * a
。例如对于数字21255
的十位,当该位取1时,剩下的位可以取值为[0,2125]
。
/* 1049 Counting Ones (30 分) */
/* 排列组合 */
#include<stdio.h>
#include<stdlib.h>
int main() {
int n;
scanf("%d", &n);
int left, right, digit = 1,num,count = 0;//高位,低位,当前分位,当前位数字,1的总数
while (n / digit) {
left = n / (digit * 10);
right = n % digit;
num = n / digit % 10;
if (num == 0) count += left * digit;
else if (num == 1) count += left * digit + right + 1;
else count += (left + 1) * digit;
digit *= 10;
}
printf("%d\n", count);
return 0;
}
当然,之前也遇到过数1~n中包含的0-9每个数字的个数UVA 1225 Digit Counting。可以用暴力求解,复杂度 O ( n ) O(n) O(n),也可以用上述方法求解,复杂度 O ( l e n ( n ) ) O(len(n)) O(len(n))。