PAT 甲级 1049(30 分)

PAT 甲级 1049(30 分)

题目

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.

Input Specification:

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

Output Specification:

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

Sample Input:

12

Sample Output:

5

思路

也即计算小于等于N的数中含有1的个数。这题我想到了动态规划,以9,99,999,9999,.....9, 99, 999, 9999,.....9,99,999,9999,.....为界划分数据,即i=0i = 0i=0时,以9为界,此时显而易见c[0]=1c[0] = 1c[0]=1,当i=1i = 1i=1时,以99为界,这时需要分析一下,在[0,9][0, 9][0,9]范围内,1的个数为1,也即c[0]c[0]c[0],而在[10,19][10, 19][10,19]的范围内可以看见十位都有1,而个位的1的个数刚好是c[0]c[0]c[0],所以1的的个数是10+c[i−1]10 + c[i - 1]10+c[i1],在[20,99][20, 99][20,99]的范围内,只能是个位上有c[0]c[0]c[0]个1,所以共有8∗c[0]8 * c[0]8c[0]个。故c[1]=c[0]+c[0]+10+8∗c[0]c[1] = c[0]+c[0]+10+ 8*c[0]c[1]=c[0]+c[0]+10+8c[0]。以999为界的一样的分析,在[0,99][0, 99][0,99]范围内的1的个数为c[1]c[1]c[1]个,在[100,199][100, 199][100,199]范围内百位上都有1,共100个,十位个位的1的个数又恰好是c[1]c[1]c[1]个,在[200,999][200, 999][200,999]范围内只能在十位个位上出现1,所以刚好又是c[1]∗8c[1]*8c[1]8个,故总的个数为c[2]=c[1]+100+c[1]+c[1]∗8c[2] = c[1]+100+c[1]+c[1]*8c[2]=c[1]+100+c[1]+c[1]8。故可以得出当i>0i > 0i>0时,c[i]=10∗c[i−1]+10ic[i] = 10*c[i - 1] + 10^ic[i]=10c[i1]+10i

而对于一个随机的数x,找出它的上界,例如15的上界是10, 345的上界是100。找到之后在低于上界的部分由c[i]c[i]c[i]数组就能得到。而高于上界的部分则按照刚刚的规则再做一遍,直到数小于10为止。若最后数大于1,那么还需要再额外加一个。

AC代码

#include <iostream>

using namespace std;

int main() {
    int n;
    cin >> n;
    int count = 0;
    int tmp = 1;
    int c[9];
    c[0] = 1;
    int x = 1;
    //计算c数组
    for (int j = 1; j < 9; j++) {
        x = x * 10;
        c[j] = c[j - 1] * 10 + x;
    }
    //得到上界,由于数小于2^30,则完全可以达到10^9(通过提交代码测出测试点6应该是大于10^9次方的),10^10是越界的,故需要处理一下越界的数
    int i = 0;
    for (i = 0; tmp <= n && i < 9; i++) {
        tmp *= 10;
    }
    if (i < 9 || (i == 9 && n < tmp)) {
        tmp = tmp / 10;
        i--;
    }
    //循环处理
    while (n >= 10) {
        int a = n / tmp;
        n = n - tmp * a;
        if (a == 1) {//如果数是以1开头
            count += (c[i - 1] + n + 1);
        }
        else if(a > 1){//如果数的开头大于1
            count += (c[i - 1] * 2 + tmp + (a - 2) * c[i - 1]);
        }
        tmp = tmp / 10;
        i--;
    }
    if (n >= 1) {
        count++;
    }
    cout << count << endl;

	system("pause");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值