1到n中1的出现次数

1到n中1的出现次数

题目描述

给定一个整数n,返回从1到n的数字中1出现的个数。

例如:

n = 5 , 1 ∼ n n=5, 1 \sim n n=5,1n为1, 2, 3, 4, 5。1,2,3,4,5。那么1出现了1次,所以返回1。

n = 11 , 1 ∼ n n=11, 1 \sim n n=11,1n为1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11。1,2,3,4,5,6,7,8,9,10,11。那么1出现次数为1(出现1次),10(出现1次),11(有两个1,所以出现了2次),所以返回4。

输入描述:

输入一个整数N。

输出描述:

输出一个整数表示答案

示例1
输入
5
输出
1
示例2
输入
11
输出
4
示例3
输入
2345
输出
1775
备注:

1 ⩽ N ⩽ 1 0 13 1 \leqslant N \leqslant 10^{13} 1N1013


解法一(计数问题):

由低位到高位考虑每一位上出现 1 的次数。假设数字为 12x45 ,百位为 x ,分情况讨论 x:

  1. 若 x==0 ,那么百位为 1 的数字有:[00~11] 1 [00~99],共有 12 * 100 = 1200种;
  2. 若 x==1,那么百位为 1 的数字有:[00~11] 1 [00~99] + 121[00~45],共有 12*100 + 46 = 1246种;
  3. 若 x>1,那么百位为 1 的数字有:[00~12] 1 [00~99],共有 13 * 100 = 1300种

于是,我们可以把一个数字拆分成三部分:left cur right,根据 cur 的情况选择上述讨论计算即可,假设 cur 为第 i 位(从0开始):

  1. 若 cur==0 , s u m + = l e f t ∗ 1 0 i sum += left * 10^i sum+=left10i
  2. 若 cur==1, s u m + = l e f t ∗ 1 0 i + r i g h t + 1 sum += left * 10^i + right + 1 sum+=left10i+right+1
  3. 若cur > 1, s u m + = ( l e f t + 1 ) ∗ 1 0 i sum += (left + 1) * 10^i sum+=(left+1)10i
解法一代码:
#include <cstdio>

using namespace std;

typedef long LL;

int main(void) {
    LL n;
    scanf("%ld", &n);
    LL high = n / 10, cur = n % 10, low = 0;
    LL digit = 1;
    LL ret = 0;
    while ( high || cur ) {
        ret += high * digit;
        if ( cur == 1 ) ret += low + 1;
        else if ( cur > 1 ) ret += digit;
        low += cur * digit;
        cur = high % 10;
        high /= 10;
        digit *= 10;
    }
    return 0 * printf("%ld\n", ret);
}
解法二(数位DP):

参考 数位DP

解法二代码:
#include <cstdio>
#include <cstring>

using namespace std;

const int N = 14;

long f[N][N];
int nums[N];
long n;

long dfs( int pos, long cnt, bool limit ) {
    if ( pos == -1 ) return cnt;
    if ( !limit && ~f[pos][cnt] ) return f[pos][cnt];
    int up = limit ? nums[pos] : 9;
    long ans = 0;
    for ( int i = 0; i <= up; ++i )
        ans += dfs( pos - 1, cnt + (i == 1), limit && (i == up) );
    if ( !limit ) f[pos][cnt] = ans;
    return ans;
}

void solve() {
    memset( f, -1, sizeof f );
    int p = 0;
    while ( n ) {
        nums[p++] = n % 10;
        n /= 10;
    }
    printf("%ld\n", dfs( p - 1, 0, true ));
}

int main(void) {
    scanf("%ld", &n);
    solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值