[编程之美] PSet2.4 统计1的数目

来源:互联网 发布:哈希算法原理 ai 编辑:程序博客网 时间:2024/04/19 03:16


问题描述:

给定一个十进制正整数N,写下从1开始,到N的所有整数,然后数一下其中出现的所有”1“的个数。

例如:

N=12,我们会写下1,2,3,4,5,6,7,8,9,10,11,12,于是出现1的个数为5.

问题是:

           1.写一个函数f(N),返回1到N之间出现“1”的个数,比如f(12) = 5;

           2.满足条件“f(N) = N”的最大N是多少?


问题一:

             解法一:考虑从1遍历到N,寻找出所有出现1的个数之和。

                                      这个算法致命伤的效率较低,时间复杂度为O(N)*计算一个整数内1的个数复杂度,对于后半部分,由于每次计算一次都缩小10倍,因此复杂度为lgN(以10为底),故算法总复杂度O(N * lgN)。

//下面的代码是计算1-N中连续的一串数字中1出现的次数//解法一:遍历1-N,寻找1出现的次数和int findOneNum(int N){    int count = 0;    int temp = 0;    for(int i=1 ; i<=N ; i++){        temp = i;        while(temp){            if(temp%10 == 1){                count++;                temp = temp/10;            }        }    }    return count;}

                   解法二:解法一硬伤是遍历,那么能否找到另外一种机制避免遍历1-N呢?可以通过归纳总结的方法找出规律进行快速计算。

下面是讨论的情况:

/*伪代码形式:以N位数为基础讨论1出现的次数情况一:假设N只有1位*/if(N >= 1) count = 1;else if(N = 0) count = 0;//情况二:假设N有2位int gewei = N%10;int shiwei = N/10;if(shiwei = 0)    count = 1;//退化到情况一else if(shiwei=1)    count = (gewei+1) + (shiwei+1);//考虑13,十位出现1为10 11 12 13,个位出现1为1 11else if(shiwei >= 2)    count = 10 + (shiwei+1);//考虑33,十位出现1为10-19,个位出现1为1 11 21 31/*情况三:假设N有3位直接考虑一个例子:113与123对于113,百位出现1为100-113,即(113-100+1),对于十位出现1次数为10-13(二位数部分),以及110-113(三位数部分)对于个位出现1为1 11 21 31 ··· 101 111总共11+1种(由十位百位决定)*//*情况四:假设N为abcde由情况三分析可以知道某一位出现1的次数与三部分有关,以百位出现1次数进行分析:由百位、低位、高位共同决定,如12013,百位为0,高位为12,低位为13*/int baiwei ;int baiwei_count;//百位给count的贡献if(baiwei = 0)// 如12013,百位出现1:100-199 1100-1199 2100-2199 ···11100-11199(高位贡献)    baiwei_count = gaowei*100;else if(baiwei = 1)//如12113,百位出现1:100-199 1100-1199···11100-11199(高位贡献)和12100-12113(低位贡献)    baiwei_count = gaowei*100 + (diwei+1);else if(baiwei >= 2)//比如12213,百位出现1:100-199 1100-1199···12100-12199(高位贡献)    baiwei_count = (gaowei+1)*100;

下面给出实现的伪代码:

//下面是实现统计1-N中1的数目的代码//使用归纳分析方法分析数字规律int sum1s(int N){    int count = 0;    int iFactor = 1;//变权因子,用于遍历N中不同位,作为本位权值    int currentBit = 0;//本位    int upperBit = 0;//高位    int lowerBit = 0;//低位    while(N/iFactor){         currentBit = (N/iFactor) % 10;//分析12306,iFactor=10,此时要拿出0,123已经6赋值         upperBit = N/(iFactor*10);         lowerBit = N - (N/iFactor)*iFactor;         switch(currentBit){            case 0:                   count += upperBit*iFactor;break;            case 1:                   count += upperBit*iFactor+lowerBit+1;break;            default:                count += upperBit*iFactor;break;        }        iFactor *= 10;    }    return count;}


       这个方法只需要对N进行处理,避开了对1-N的遍历,时间复杂度由N的长度决定,即O(N的长度),由于为十进制数据,N的长度=lgN+1;故该算法复杂度为O(lgN + 1),注意到lg以10为底。

问题二:满足条件“f(N) = N”的最大N是多少?

               这个问题依旧可以使用归纳总结的方法找规律:

//确定最大的N,下面给出分析过程/*  num         f(num)9以下         199以下      1*10+10*1=20  999以下    1*100+10*10+100*1=300  9999以下   1*1000+100*10+10*100+1*1000=4000······999 999 999以下   900 000 0009 999 999 999以下  10 000 000 000*//*设n为num的位数由数字关系可以分析出f(10^n-1) = n*10^(n-1);从f(n)递增关系可以看到当n大于一定值时,f(n)>n,如上表中f(10^10-1) > 10^10-1;所以可以从n=10^10-1开始向前追溯(向0递减),依次检查是否有f(n)=n,得出的第一个满足条件的1 111 111 110是满足f(n)=n的最大整数*/int sum1s(int N);typedef long long LLint main(){    const LL N=99999999999;    for(LL i=N ; i>0 ; i--){        if(sum1s(i)==i){            printf("%11d\n" , i);            break;        }    }    return 0;}





0 0
原创粉丝点击