poj-3286

//stupid method!!!!!!!!!!!!!!!
//388K  360MS   G++
#include <stdio.h>
#include <string.h>
#include <math.h>

int C[33][33]; // C[n][m], choose m from n;

void getCombination() {
    for (int n = 0; n <= 32; n++) {
        for (int m = 0; m <= n; m++) {
            if (m == 0) { // choose nothing
                C[n][m] = 1;
            } else if (n == 0) { // nothing to choose
                C[n][m] = 0;
            } else if (m > n) {
                C[n][m] = 0;
            } else if (m == n) {
                C[n][m] = 1;
            } else {
                C[n][m] = C[n-1][m] + C[n-1][m-1];
                // printf("C %d %d is %d\n",n, m, C[n][m]);
            }
        }
    }
}

char str1[20];
char str2[20];

int getHowManyZero(char * num) {
    int zeroNum = 0;
    int length = strlen(num);
    for (int i = 0; i < length; i++) {
        if (num[i] == '0') {
            zeroNum++;
        }
    }
    return zeroNum;
}

long long getZeroNum(char * num) {
    int length = strlen(num);
    long long totalZeroNum = 1; // 0 has 1 '0'

    // step 1, all value's length < num
    for (int curLength = 1; curLength <= length-1; curLength++) {
        for (int zeroNum = 1; zeroNum <= curLength - 1; zeroNum++) {
            totalZeroNum += zeroNum*
                        C[curLength - 1][zeroNum]*pow(9, curLength - zeroNum);
        }
    }

    // printf("S1 %lld\n", totalZeroNum);

    //step2, all value's length == num's length, first pos val < num's first pos val
    char firstChar = num[0];
    int possibleMostChoose = firstChar - '1';
    if (possibleMostChoose > 0) {
        long long curTotalNum = 0;
        for (int zeroNum = 1; zeroNum <= length-1; zeroNum++) {
            curTotalNum += zeroNum*C[length-1][zeroNum]
                            *pow(9, length-1 - zeroNum);
        }
        totalZeroNum += possibleMostChoose * curTotalNum;
    }

    // printf("S2 %lld\n", totalZeroNum);

    int beforeZeroNum = 0;
    for (int curPos = 1; curPos <= length-1; curPos++) { // from the 2nd char to last char
        char curChar = num[curPos];
        int possibleMostChoose = curChar - '0';

        if (possibleMostChoose > 0) {
            long long curTotalNum = 0;
            if (possibleMostChoose-1 > 0) {            
                for (int zeroNum = 0; zeroNum <= length - curPos -1; zeroNum++) {
                    curTotalNum += (zeroNum + beforeZeroNum)*C[length - curPos -1][zeroNum]
                                *pow(9, length - curPos - 1 - zeroNum);
                }
                totalZeroNum += (possibleMostChoose-1) * curTotalNum;
            }
            
            // for '0'
            curTotalNum = 0;
            if (length - curPos -1 >= 1) {            
                for (int zeroNum = 0; zeroNum <= length - curPos -1; zeroNum++) {
                    curTotalNum += (zeroNum + 1 + beforeZeroNum)*C[length - curPos -1][zeroNum]
                                *pow(9, length - curPos - 1 - zeroNum);
                }
            } else { // last pos
                // curTotalNum +=  (possibleMostChoose-1) * beforeZeroNum;
                curTotalNum += (beforeZeroNum + 1);            
            }
            totalZeroNum += curTotalNum;

        } else if (possibleMostChoose == 0) {
            beforeZeroNum++;
        }
    }

    // printf("S3 %lld\n", totalZeroNum);

    return totalZeroNum;
}

int main() {
    getCombination();
    while(scanf("%s %s", str1, str2) != EOF) {
        // printf("%s %s\n", str1, str2);
        if (str1[0] == '-') {
            return 0;
        }
        long long nZeroNum = getZeroNum(str2);
        int zeroNumEnd = getHowManyZero(str2);
        if (str1[0] != '0') {
            long long mZeroNum = getZeroNum(str1);
            printf("%lld\n", nZeroNum - mZeroNum + zeroNumEnd);
        } else {
            printf("%lld\n", nZeroNum + zeroNumEnd); 
        }
    }
}

用了非常笨的办法来得到0的个数,是从排列组合的角度出发, 对当前数字的每一位进行检查,遍历比起小并且含有0的数,累加0的个数,但是这种朴素的思路会有很多特殊情况,到最后完全就是一堆逻辑的堆砌,十分难看,贴一个容易理解的别人的题解:


From:http://www.cnblogs.com/zhj5chengfeng/archive/2013/03/24/2977984.html

首先转化成求 [0, x] 中所有数中,含有的 0 的个数

那么对于一个数 x,怎么求出从 0 到 x 中所有数含有 0 的个数的和呢?

我们可以限制每一位是 0,然后再来计算。举个例子,假设 x 是 21035

        首先 0 肯定是一个,sum 赋初值为 1

        个位数是 0

                个位数前面的数不能是 0,能够取的数就是 [1, 2103],个位后面没有了

                那么 sum+=2103*1

        十位数是 0

                十位数前面的数不能是 0,能够取的数就是 [1, 210],由于 0 比 3 小,十位后面可以取 [0, 9]

                那么 sum+=210*10

        百位数是 0

                百位数前面的数不能是 0,能够取的数就是 [1, 21],由于 0 是等于 0 的,这里就要特殊处理下了:

                百位数前面的数如果是在 [1, 20] 中的,百位数后面的数可以取的就是 [0, 99]

                百位数前面的数如果取的是 21,百位数后面的数就只能取 [0, 35]

                那么,sum+=20*100+36

        ......

相信手推到这里了,读者已经能够明白怎么操作

这种思路 细化到每个位,取0时,能够得到的比数N小的数的个数(也就是该位0的个数),然后利用加法原理进行累加,逻辑比较简明,但是还是有特殊情况处理的.

http://www.cnblogs.com/pcoda/archive/2012/04/25/2470256.html

比如算4123中有多少个2 

按位统计,,,先算各位,,个位是2的情况有413种,,,因为各位左边可以0~412,,,而右边没有数字,,,

然后是十位,,,十位是2的有41*10 + 1*4种,,当左边从0~40时,,,右边可以从0~9,,,而左边为41时,,右边只能从0~3

然后是百位,,,,百位有4*100种,,,,即左边从0~3,,右边从0~99

千位有  1*1000,,,左边没有数字,,,右边0~999,,,,

上面是计算1~9,,,,计算0的时候比较特殊,,,,原因是除了0这一个数字之外,,,,0不能做开头,,,

可以看到在求1~9的个数的时候,,,都是分为2部分相乘,,,这样0的处理也很简单,,只需把相乘的左半部分-1,,,,


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值