1的数目

编程之美2.4节 1的数目

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

例如:

N=2, 写下1, 2.这样只出现1个“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是多少?

 

只针对第一个问题,详细分析如下:

通过求解小于N的数在每一位上可能出现的1的次数之和得到最终结果。

 

先看1位数的情况:

如果N= 3,那么从1到3的所有数字:1,2,3, 只可能各位数上出现1,而且只出现1次。进一步可以发现如果N是个位数,且N>=1,那么f(N)=1,如果N=0, 则f(N)=0.

 

再看2位数的情况:

如果N=12, 那么从1到12的所有数字:1,2,3,4,5,6,7,8,9,10,11,12,个位和十位数字上都次数也有可能有1,我们可以分爱考虑,个位出现1的次数有两次:1和11, 十位出现的次数有3次:10,11,12, 所以f(N)=2+3=5。要注意的是11这个数字在十位和个位读出现了1。但是11恰好分别被计算了一次,所以不用特殊处理。再考虑22的情况,它和12不同,十位出现1的次数为10次,从10到19,个位出现1的次数为1,11,21,所以f(N)=3+10=13。通过对两位数进行分析,我们发现,个位数出现1的次数不仅和个位数字有关,还和十位数字有关:如果N的个位数字大于等于1,则个位出现1的次数为十位数的数字加1如果N的个位数为0,则个位出现1的次数等于十位数的数字。而十位数上出现1的次数也累死:如果十位数字等于1,则十位数上出现1次数为个位数的数字加1;如果十位数大于1,则十位数上出现1的次数为10.

 

3位数的情况:

如果N=123:

个位数出现1的个数为13: 1,11,21, ... , 91,101,121

十位出现1的个数为20: 10~19, 110~119

百位出现1的个数为24: 100~123

f(123)=个位出现1的个数+十位出现1的个数+百位出现1的个数=13+20+24=57.

 

同理,我们可以再分析4位数,5位数等等。

 

根据上面的分析,我们可以推广到一般情况,从N得到f(N)的计算方法:

假设N=abcde, 这里a、b、c、d、e分别是十进制数N的各个数位上的数字。如果要算百位上出现1的个数,它将受到三个因素的影响:百位上的数字,百位以下(低位)的数字,百位以上(高位)的数字。

如果百位上的数字为0,可知,百位上可能出现1的次数由更高位决定,比如12013,百位出现1的情况可能是100~199,1100~1199,2100~2199,..., 11100~11199,一共有1200个。也就是由更高位数字(12)决定,并且等于更高位数字(12)X 当前位数(100)。

如果百位上数字为1,可知,百位上可能出现1的次数不仅受高位影响,还受低位影响。例如对于12113,受更高位影响,百位出现1的情况100~199,1100~1199,2100~2199,..., 11100~11199,一共有1200个。等于更高位数字(12)X  当前位数(100)。还受低位影响,百位出现的情况是12100~12113,一共114个,等于低位数字(113)+1。

如果百位数字大于1(即2~9),则百位上可能出现1的次数仅由更高位决定,比如12213,百位数字出现1的可能性为:100~199,1100~1199,2100~2199,..., 11100~11199, 12100~12199,一共1200个,并且等于更高位数字+1(12+1)X 当前位数(100)。

 

通过上面的分析,具体源码实现如下:

/
// Find the number of 1 in the integers between 1 and n
// Input: n - an integer
// Output: the number of 1 in the integers between 1 and n
/
int Sum1s(int n)
{
	int higherNum = 0;  // 高位数字
	int currNum = 0;    // 当前位数字
	int lowerNum = 0;   // 低位数字

	int iCount = 0;     // 记录1的个数 
	int iFactor = 1;    // 用于循环,求解每一位上1的数字

	while(n/iFactor != 0)
	{
		lowerNum = n - (n/iFactor)*iFactor;
		currNum = (n/iFactor)%10;
		higherNum = n/(iFactor*10);

		switch(currNum)
		{
		case 0:
			iCount += (higherNum * iFactor);
			break;
		case 1:
			iCount += (higherNum * iFactor + lowerNum + 1);
			break;
		default:
			iCount += ((higherNum+1) * iFactor);
			break;
		}

		iFactor *= 10;
	}

	return iCount;
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值