/*
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是多少?
剑指解法:
需要用递归,拆分成三部分:最高位是1 + 除最高位剩余的位数的1 + 递归的1
最高位不是1
解法1:
从1开始遍历到N,将其中的每一个数含有1的个数加起来,
解法2:
给定N,分析小于N的数在每一位上可能出现1的次数之和来得到这个结果,对于一个特定的N,分析其中规律:
先看1位数的情况:
如果N=3,那么从1到3的数字是:1,2,3,只有个位数字上可能出现1,而且只出现1次,进一步可以发现如果N是个位数,且N>=1,那么f(N)都等于1
规律:如果N是个位数,且N>=1,f(N) = 1,如果N=0,f(0) = 0
再看两位数的情况:
如果N=13,那么从1到13所有数字为:1,2,3,4,5,6,7,8,9,10,11,12,13,个位和十位的数字上都可能是1。个位出现1的次数有两次:1和11
十位出现1的次数有4次:10,11,12,13,所以f(N) = 2 + 4 = 6.11这个数字个位和十位都是1,但是11恰好被计算了两次。
N=23,十位出现1的次数为10次,从10~19,个位出现1的次数为1,11,21,所以f(23) = 10 + 3 = 13
个位数出现1的次数不仅和个位数字有关,还和10位数字有关;如果N的个位数>=1,则个位出现1的次数为十位数的数字加1
= 0,则个位出现1的次数为十位数的数字
十位数=1,则十位数上出现1的次数为个位数的数字加1
十位数>1,则十位数上出现1的次数为10
分析3位数:
N=123
个位出现1的个数为13:1,11,21,...,91,101,111,121
十位出现1的个数为20:10~19,110~119
百位出现1的个数为24:100~23
假设N=abcde,这里a,b,c,d,e分别是十进制数N的各个数位上的数字。如果要计算百位上出现1的次数,它收到3个因素的影响,百位上的数字,百位以下,百位以上
如果百位 = 0,百位上出现1的次数由更高位决定:比如12013,百位出现1的情况可能是100~199,1100~1199,2100~2199,。。。,11100~11199,一共有1200个
由更高位的数字12*当前位数100
如果百位上的数字为1,百位出现1的次数不仅受更高位影响,还受低位影响,比如12113,受更高位影响,百位出现1的情况时100~199,1100~1199,2100~2199,。。。,
11100~11199,gong 1200个,等于更高位数字12*当前位数。
受低位影响,百位出现1的情况时12100~12113,等于低位数字113+1
如果百位上数字大于1(即为2~9),则百位上出现1的次数仅由更高位决定,比如12213,则百位上出现1的可能性为:100~199,1100~1199,2100~2199,...,11100~11199,
12100~12199共1300个。=更高位数字加1*当前位数= (12+1)*100
问题2的解法:
要确定最大的数N,满足f(N) = N,分析
9以下为:1个
99以下为:10*1 + 10*1 = 20
999以下为:(99+1)*1 + (9+1)*10 + (0+1)*100 = 100 + 100 + 100 = 300
9999以下为:(0+1)*1000+10*300 = 4000
999 999 999 以下为:900 000 000个
9 999 999 999 以下为:10 000 000 000个
归纳得:f(10^n-1) = n*10^(n-1)
当n>10^10 - 1,f(n)的值大于n,可以猜想,当n大于某一个数N时,f(n)会始终比n大,也就是说最大满足条件在0~N之间,如果能估计出这个N,只要让n从N往0递减,
第一个满足的的数就是要求的整数
问题转化为如何证明上界N存在,并估计该上界N。
令N=10^11-1=99 999 999 999,让n从N往0递减,依次检查是否有f(n) = n,第一个满足条件的就是我们要找的整数。得出n = 1 111 111 110,是满足f(n) =n的最大整数
输入:
2
12
23
123
输出:
1
5
13
57
*/
/*
关键:
1 lLow = n - (n/lFactor)*lFactor;//参见123 - (123/10)*10
lCur = (n / lFactor) % 10;//参见(123/10)%10
lHigh = n / (lFactor * 10);//参见123 / (10*10)
2 case 0: lCount += lHigh * lFactor;//如果当前位为0,那么当前位出现1的次数 = 高位*位数
break;
case 1: lCount += lHigh * lFactor + lLow + 1;//如果当前位为1,那么当前位出现1的次数 = 高位 * 位数 + 低位 + 1
break;
default: lCount += (lHigh + 1) * lFactor;//如果当前位>=2,那么当前为出现1的次数 = (高位+1)*位数
3 当n>10^10 - 1,f(n)的值大于n,可以猜想,当n大于某一个数N时,f(n)会始终比n大,也就是说最大满足条件在0~N之间,如果能估计出这个N,只要让n从N往0递减,
第一个满足的的数就是要求的整数
问题转化为如何证明上界N存在,并估计该上界N。
令N=10^11-1=99 999 999 999,让n从N往0递减,依次检查是否有f(n) = n,第一个满足条件的就是我们要找的整数。得出n = 1 111 111 110,是满足f(n) =n的最大整数
long long lN = 1e11 - 1;
*/
#include <stdio.h>
int count1(int n)
{
int iRet = 0;
do
{
iRet += ( (n % 10 == 1) ? 1 : 0 );
n /= 10;
}while(n);
return iRet;
}
long long count1_divide(int n)
{
long long lCount = 0;
long long lHigh,lLow,lCur;
lHigh = lLow = lCur = 0;
long long lFactor = 1;
while(n / lFactor)
{
lLow = n - (n/lFactor)*lFactor;//参见123 - (123/10)*10
lCur = (n / lFactor) % 10;//参见(123/10)%10
lHigh = n / (lFactor * 10);//参见123 / (10*10)
switch(lCur)
{
case 0: lCount += lHigh * lFactor;//如果当前位为0,那么当前位出现1的次数 = 高位*位数
break;
case 1: lCount += lHigh * lFactor + lLow + 1;//如果当前位为1,那么当前位出现1的次数 = 高位 * 位数 + 低位 + 1
break;
default: lCount += (lHigh + 1) * lFactor;//如果当前位>=2,那么当前为出现1的次数 = (高位+1)*位数
}
lFactor *= 10;
}
return lCount;
}
long long findN()
{
long long lN = 1e11 - 1;
//long long lN = 1111111110;
while(lN >= 0)
{
if(lN == count1_divide(lN))
{
return lN;
}
lN--;
}
}
void process()
{
int n;
while(EOF != scanf("%d",&n))
{
//int iRet = 0;
//for(int i = 1 ; i <= n ; i++)
//{
// iRet += count1(i);
//}
//printf("%d\n",iRet);
printf("%lld\n",count1_divide(n));
}
}
void process2()
{
printf("%lld\n",findN());
}
int main(int argc,char* argv[])
{
process2();
//process();
getchar();
return 0;
}