题目:

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

要求:

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

  在32位整数范围内,满足条件的“f(N) =N”的最大的N是多少?

 

设计思想:

(解法一)

  开始想到了一个最简单的方法来计算f(N),那就是从1开始遍历,直到N结束,把其中每一个数中含有“1”的个数加起来,结果就是从1到N所有“1”的个数的和。这个方法很简单,但算法的实现效率是个大问题,如果N很大,则需要很长的运算时间才能得到计算结果。

(解法二)

   找了一些资料,发现这也是编程之美里的一道题,得到一点启发。首先列出一些数字的情况,来找到其中隐藏的规律:

一位数: f(0)=0、f(1)=1、f(2~9)=1

两位数(以位数是3的为例,[十位]+[个位]):

f(13)=4+2=6、f(23)=10+3=13 …… f(93)=10+10=20

 通过分析发现,个位数出现1 的个数和个位数字与十位数有关:如果N的个位数大于等于1 ,则个位出现1的个数为十位数的数字加1;如果N的个位数的数字小于1,则个位出现1的个数为十位数的数字。十位出现1的次数:如果十位数字等于1,则十位出现1的个数为个位数字加1;如果十位数大于1,则十位出现1的个数为10次。

三位数([百位]+[十位]+[个位]):

f(103)=4+10+11=25、f(113)=14+14+12=40、f(123)=24+20+13=57……

f(193)=94+20+20=134、f(203)=100+20+21……

同理分析四位数、五位数……

 

(1)

源代码:

#include<iostream.h>
int f(int n)
{
    int i,unit,decade;
    int count=0;
    for(i=1;i<=n;i++)
    {
        decade=i;
        while(decade!=0)
        {
            unit=decade%10;
            decade=decade/10;
            if(unit==1) 
            {
                count++;
            }
        }
    }
    return count;
}

void main()
{
    int n,count;
    cout<<"Please input N: ";
    cin>>n;
    count=f(n);
    cout<<"From 1 to "<<n<<",there are "<<count<<" ones."<<endl;
}
View Code

运行结果:

 

 

(2)

源代码:

#include <iostream>
#include <cstdio>
using namespace std;

int f(int n)
{
    int factor=1;
    int count=0;
    int high=0;
    int current=0;
    int low=0;
    while(n/factor)
    {
        low=n%factor;
        current=n/factor%10;
        high=n/factor/10;
        switch(current)
        {
            case 0:
            count+=high*factor;
            break;
            case 1:
            count+=high*factor+low+1;
            break;
            default:
            count+=(high+1)*factor;
            break;
        }
        factor*=10;
    }
    return count;
}
int main()
{
    int n ;
    while(scanf("%d",&n)!=EOF)
    {
        cout<<f(n)<<endl;
    }
    return 0;
}
View Code

运行结果:

 

 

总结:

   如果只要求实现基本功能,那实际并不是很难,关键在于当N变得很大时,遍历绝对不是一个好方法,那么就要找到其中隐藏的规律,就像小学奥数一样,在一列数字中,把每个情况都拆开,然后分析,找到隐含的规律。在发现d过程可能要列举很多情况才能发现,也才能肯定最先找到的规律是否正确。不是到确认为止,还要继续扩展,四位数、五位数、六位数……甚至更大,再者就是不确定的N,需要它具有任意性、通用性,而不是单单的特殊的一个数字,类似“abcde”。

   最近老师给的题目都来自《编程之美》里面,题目很典型,主要都是讲究思路和算法,有机会要看看这本书。