题目大意:
给出一个数字N,求1~N的所有数字中出现1的个数
算法思路:
本人采用了暴力遍历求解,但是只得了22分,参考了算法笔记的思路,直接写在下面考虑30710这个数:
首先考虑末尾为0
我们计算末尾有多少个数是覆盖了1的,那么末尾为1的时候前面是只能取0~3070,因为如果取3071,那么就是30711>30710了,共有3071种选择
然后考虑倒数第二位1
它为1,那么前面为0~306的时候,后面一位随便取都行,当前面为307时,前四位为3071,末尾只能取0,那么有306*10+1种取法
然后考虑倒数第三位
它为7,如果它为1的话,前面后面都可以随便取,因为它为7比1要大,只要他们组成的数不大于30710,都是有效的,那么取法共有$(30+1)*10^2$种
同理倒数第四位为0
那要令它为1,前面只能取0~2,后面可以任取,共有$3*10^3$种
最后倒数第五位
它是3,比1要大,所以它为1时,后面可以任取,共有$10^4$种
总结
如果把某个数左边取为leftNum,右边取为rightNum,那么得到的结果有三种可能,expo是当前数字的位数次方(例如123中的2,其位数为1,对应的expo就为10^1);
1.当前位为0,count += leftNum * expo;
2.当前位为1,count += leftNum*expo+rightNum+1;
3.当前为>1,count += (leftNum+1)*expo
注意点:
1、经过测试,PAT的数据最大数字不超过5位数字,所以数组开到4就好了。
提交结果
AC代码:
#include <cstdio>
#include <algorithm>
using namespace std;
int a[4] = {};
int main(){
int N,n;
scanf("%d",&N);
n = N;
int index = 0;
while(N!=0){
a[index++] = N%10;
N /= 10;
}
// 逆置
for (int i = 0; i < index/2; ++i) {
swap(a[i],a[index-i-1]);
}
int count = 0;//统计1的个数
int expo = 1;// a[i]对应的10^i
int leftNum = n/10;
int rightNum = 0;
for (int i = index-1; i >= 0; --i) {
if(a[i]>1){
// 当前位大于1
count += (leftNum+1)*expo;
} else if(a[i]==1){
count += leftNum*expo+rightNum+1;
} else {
count += leftNum * expo;
}
expo *= 10;
rightNum = n%expo;
leftNum /= 10;
}
printf("%d",count);
return 0;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。