PAT甲级 1049 Counting Ones(30) (数位DP)

题目

The task is simple: given any positive integer N, you are supposed to count the total number of 1's in the decimal form of the integers from 1 to N. For example, given N being 12, there are five 1's in 1, 10, 11, and 12.

输入

Each input file contains one test case which gives the positive N (≤230).

输出

For each test case, print the number of 1's in one line.

样例输入 

12

样例输出 

5

题意理解

题意太简单了。。。但是题目就不简单了。。。

给你一个数字 问你从1到这个数字之间

一共出现了多少个 1 比如 样例这个是 12

1 2 3 4 5 6 7 8 9 10 11 12 

1有一个1 10有一个1 11有两个1 12有一个1 那么1到12总共有5个1

那么统计这个数字从1到这个数字出现了多少位的话

我们可以用经典的数位DP来分析

我们举一个例子

比如这个数是 xxx1yyy 

当我们要统计数位的时候 具体统计的某个数位是什么 比如这题统计的是1的个数 以下用 表示

我们先将一个数划分为左右两边 从左到右 左边是最高位 右边是最低位 

先来看左边 也就是xxx

  1. 当i不为0时 xxx: 0...0~l - 1 也就是 l *(右边的数的位数)== l*p10
  2. 当i是0的时候 因为不能有前导0 所以 xxx: 0...1~l - 1 也就是 (l-1) *(右边的数的位数)== (l - 1)*p10

再来看右边 也就是当xxx ==l 时 也就是yyy

  1. i>dj  0种选法
  2. i==dj 刚刚好要的那个数就是dj位上的数 那么就有后面位数的所有选法 就是 0...0~r  也就是 r+1 种 也就是 把低位的全部选上 也就是加上r+1种选法
  3. i<dj  0...0~9...9  也就是加上 10^(右边的数的位数) 种选法 也就是 p10种选法

最后全部加起来就是个数了

代码 

#include <bits/stdc++.h>
using namespace std;

int get(int x){
    int cnt=0;
    while(x){
      cnt++;x/=10;
    }
    return cnt;
}
int power10(int x){
    int res=1;
    while(x--){
        res*=10;
    }
    return res;
}
int count(int n,int i){
    int res=0,dgt=get(n);//dgt 一个数有几位
    //从最高位遍历到最后一位 比如一个数是123456 那就从1开始遍历到6
    for(int j=1;j<=dgt;j++){
       int p10=power10(dgt-j);
       //l 是第i位的左边的数 r为右边的数 dj是刚好是j位的数
       int l=n/p10/10,r=n%p10,dj=n/p10%10;
       /*
       因为这里是找1 所有i肯定不是0
       那么我们就有0...0~l-1 即 l*10^(右边的数的位数) 就是 l*p10种选法
       */
       res+=l*p10;
       /*
        (1) i>dj  0种选法
        (2) i==dj 刚刚好要的那个数就是dj位上的数 那么就有后面位数的所有选法
        就是 0...0~r r+1种
        也就是 把低位的全部选上 也就是加上 r+1种选法
        (3) i<dj 0...0~9...9   也就是加上 10^(右边的数的位数) 种选法 也就是 p10种选法
       */
       if(i==dj)res+=r+1;
       else if(i<dj)res+=p10;
    }
    return res;
}
int main(){
    int x;
    cin>>x;
    cout<<count(x,1)<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值