代码转载自http://blog.youkuaiyun.com/winkloud/article/details/7866520
看了好久才看懂
举449为例,len=3,因为要考虑1-449,而下面的算法都是在以一种小于的关系而不是小于等于的关系存在,因此要写成450才会计算1-449,。所以n++是这么来的。
①i=3,先统计400以下的(1-399),即0-99中有49的,再乘上num[i]即可,(ans+=dp[i-1][2]*num[i]; ),即49、149、249、349,所以乘以num[i](此时为4)。
再统计前一位是9开头的,如果num[i]>4,则可以在9前面补上一个4,所以(ans+=dp[i-1][0]*num[i]; )(此时不会执行,因为num[i]=4)。【注意不能num[i]>=4,因为如果是49x的话,49x已经超出449的范围了,所以说只是统计1-399中的,400+的先不能考虑,放在后面】
②接下来考虑400-449的,即考虑0-9中有多少个含有49的,同样先试试有没有49的,显然没有。因为num[i]=5,所以考虑在9前面补4的情况,即49。
这里面还有一种特殊情况的考虑,n++后,上限n中有49出现时,后面的就不用考虑了,不论包含或者不包含49都满足条件。
如果不包含这个条件考虑的话,举4984为例,++后为4985,先考虑1-3999,后4000-4899,后900-979,后80-84。其中900-979以及80-84的时候,在前面是49时是成立的,不用考虑,若没有包含这个条件考虑的话则少算了这部分的数据。
#include<string.h>
#include<stdio.h>
long long dp[20][3];
int num[20];
int main()
{
memset(dp,0,sizeof(dp));
dp[0][0] = 1;
for(int i = 1;i<= 20;i++){
dp[i][0]=dp[i-1][0]*10-dp[i-1][1]; //dp[i][0] 表示i位数字中不含49的数字的个数
dp[i][1]=dp[i-1][0]; //dp[i][1] 表示i位数字中以9开头的数字的个数
dp[i][2]=dp[i-1][2]*10+dp[i-1][1];//dp[i][2] 表示i位数字中含有49的数字的个数
}
int t;
scanf("%d",&t);
while(t--)
{
int len = 0,last = 0;
long long ans = 0;
long long n = 0;
scanf("%I64d",&n);
n++;
memset(num,0,sizeof(num));
while(n){
num[++len]=n%10;
n/=10;
}
bool flag=false;
for(int i=len;i>=1;i--)
{
ans+=dp[i-1][2]*num[i];
if(flag)
{
ans+=dp[i-1][0]*num[i];
}
if(!flag && num[i]>4)
{
ans+=dp[i-1][1];
}
if(last==4 && num[i]==9)
{
flag=true;
}
last=num[i];
}
printf("%I64d\n",ans);
}
}