一开始直接2分钟一个莽夫写法,很明显有两个测试点超时。
# include <iostream>
# include <algorithm>
using namespace std;
int N;
int cnt = 0;
int main(void)
{
cin >> N;
for(int i=1;i<=N;++i){
string stri = to_string(i);
cnt += count(stri.begin(), stri.end(), '1');
}
cout << cnt;
return 0;
}
很厉害的写法,不太能想得到
# include <iostream>
# include <algorithm>
using namespace std;
int main(void)
{
int N;
int left, now, right;
int cnt = 0; //记录结果
int a = 1; // 用来控制遍历到数字的最后一位的,例如数字30710,当遍历到第一位时为00001 …… 第四位时是01000,第五位时是10000,结束时为100000 > 30710
cin >> N;
// 从低位到高位遍历,统计在在1~N的过程中该位为1有多少种情况
while(a <= N) // 晴神宝典上的是while(a/n != 0) 我觉得我这种更容易理解点……
{
left = N / (a*10); // 当前位的左边的数
now = (N / a) % 10 ; // 当前位的数
right = N % a; // 当前位右边的数
// 比如30710的第四位要为1时(其却等于0),left * a就为01XXX~21XXX(0~2有3种,所以就为left )(XXX为000~999,有10^3中情况,也就是a)
if(now == 0)
cnt += left * a;
// 比如30710的第二位要为1时(其刚好为1),left * a 就为0001X~3061X的情况(000~306有307种,所以就为left)(X为0~9,有10^1种情况,也就是a)
/* 考虑到高三位为307时,因为当前位刚好为1,所以不能有3071X的情况(在这种情况X不能任取0~9)
如果数为30710: right+1 就为30710~30710,因为right为0,所以为 0+1 = 1
如果数为30715: right+1 就为30710~30715,因为right为5,所以为 5+1 = 6)
*/
else
if(now == 1)
cnt += left * a + (right+1);
// 比如30710的第三位要为1时(其却大于1),left + 1 是为001XX~301XX的情况(00~30有31种,也就是left+1种)(XX为00~99,有10^2种情况,也就是a)
/* 考虑到高三位为30时,因为当前位的7大于等于2,所以可以有301XX的情况下(在这种情况XX可以任取00~99)
*/
else
cnt += (left+1) * a;
a *= 10; // 乘10以去到下一位
}
cout << cnt << endl;
return 0;
}