nowcoder马拉松--包含一

本文介绍了一种高效算法,用于统计指定范围内包含数字1的正整数个数。通过对数值进行拆分并利用递归思想减少重复计算,实现了在大范围内的快速求解。
  • 时间限制:1秒空间限制:32768K
  • 通过比例:21.59%
  • 最佳记录:0 ms|8460K

题目描述

NowCoder总是力争上游,凡事都要拿第一,所以他对“1”这个数情有独钟。爱屋及乌,他也很喜欢包含1的数,例如10、11、12……。你能帮他统计一下有多少个包含1的正整数吗? 
输入描述:
输入有多组数据,每组数据包含一个正整数n,(1≤n≤2147483647)。


输出描述:
对应每组输入,输出从1到n(包含1和n)之间包含数字1的正整数的个数。

输入例子:
1
9
10
20

输出例子:
1
1
2
11

简要分析:
此题从题目意思来看非常简单,第一想法就是暴力求解的方法,去计算每一个数是否有包含1。但是这样的话很多情况就重复计算了。
例如,100包含1,101也包含1,这两个数的最高位都包含1,也就是说,若最高位包含1,则无论其余位是否包含1,则该数都是包含1。
因此若采用暴力求解,当n超过100 000 000的时候,运行时间就不符合要求了。
我的解决思路就是,把一个数拆成两半,若前一半包含1,则后一半无论是什么值这个数都是包含1的。若前一半不包含1,则需计算后一半包含1的个数。我以N = 100 000为界限,暴力求解在这个数值内是可以符合要求的。
实现代码如下:
#include <iostream>
#include <stdio.h>
#include <string.h>

using namespace std;
void solver1(int n);
void solver2(int n);
bool is_containOne(int n);
int countOne(int n);

int countOne(int n)
{
    int cnt = 0;
    for (int i = 1; i <= n; i++)
    {
        if (is_containOne(i))
        {
            //判断是否包含1,true则增1
            cnt++;
        }
    }
    return cnt;
}
bool is_containOne(int n)
{
    while (n)
    {
        if (n%10 == 1)
        {
            //判断n这个数里是否包含1,是,则返回true
            return true;
        }
        n = n/10;
    }
    return false;
}

void solver2(int n)
{
    long long cnt = 0;
    int N = 100000;
    if (n <= N)
    {
        //小于N的数用暴力解法即可
        solver1(n);
    }else
    {
        //大于N的数采用分割的方式解决
        /*
        将一个数分成大于N的部分和小于N的部分,例如2147000,则分为21和47000这两部分
        则此时就可以分为大于Nde部分包含1的个数与不包含1的个数
        通过分别求出21和47000的包含1个数来计算整个数包含1的个数
        */
        int x = n/N;
        int cntx = countOne(x);//大于N的部分包含1的个数
        int cntN = countOne(N);
        int y = n%N;
        int cnty = countOne(y);//小于N的部分包含1的个数
        if (is_containOne(x))
        {
            /*
            当大于N的部分包含1,0~N的部分包含1的个数为cntN, N+1~x*N的部分包含1的个数为(cntx-1)*N
            N+1~x*N的部分不包含1的个数为(x-cntx)*(cntN-1),x*N~x*N+y的部分包含1的个数为y
            */
            cnt = (cntx-1)*N + (x-cntx)*(cntN-1) + cntN;
            cnt += y;
        }else
        {
            /*
            当大于N的部分不包含1,此时易知x>1,则N+1~x*N的部分包含1的个数为cntx*N,
            N+1~x*N的部分不包含1的个数为(x-cntx)*(cntN-1),x*N~x*N+y的部分包含1的个数为cnty
            */
            cnt = cntx*N + (x-cntx)*(cntN-1);
            cnt += cnty;
        }
//        printf("cntx = %d, cntN = %d, cnty = %d\n", cntx, cntN, cnty);
        printf("%ld\n", cnt);
    }
    return;
}

void solver1(int n)
{
    //暴力求解方法
    int cnt = countOne(n);
    printf("%d\n", cnt);
    return;
}

int main()
{
    int n;
//    printf("%d", is_containOne(100001));
    while (scanf("%d", &n) == 1)
    {
        solver2(n);
//        solver1(n);
    }
    return 0;
}

PS: 最佳纪录0秒的方法,我还不知道。。。应该可以按照这个思路继续优化吧,可能可以采用递归试试,改天有空再试试



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值