【PAT甲级、C++】1049 Counting Ones (30分)

一开始直接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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值