题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=3555
题解:
开一个dp数组,i代表当前数字的长度,dp[i][0]表示不含49的数字个数,dp[i][1]表示最高位为9且不含49的数字的个数,dp[i][2]表示满足条件的数字个数。可以推出以下的递推公式:
dp[0][0] = 1;
for(int i = 1; i <= 22; i++)
{
dp[i][0] = dp[i-1][0] * 10 - dp[i-1][1];//不含49的数字个数
dp[i][1] = dp[i-1][0];//不含49 && 最高位为9的数字个数
dp[i][2] = dp[i-1][1] + dp[i-1][2] * 10;//满足条件的数字个数
}
然后讲数字按位输入到数组里,flag代表高位有没有出现过49。首先加上a[i] * i-1位中满足条件的数的个数,如果高位出现过49,加上a[i] * i-1位中不满足条件的数的个数,如果高位没有出现过49, 且第i位 > 4,则加上剩下i-1位中以9开头的数的个数。最后判断高一位和本位是否构成49,更新flag。
注意这种处理需要将n+1,因为处理的是【1, n)的情况,并且需要将最高位的高一位位数表示为0。
AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
#include <sstream>
using namespace std;
long long int N;
int number[30];
long long int dp[25][3];
int main()
{
dp[0][0] = 1;
for(int i = 1; i <= 22; i++)
{
dp[i][0] = dp[i-1][0] * 10 - dp[i-1][1];//不含49的数字个数
dp[i][1] = dp[i-1][0];//不含49 && 最高位为9的数字个数
dp[i][2] = dp[i-1][1] + dp[i-1][2] * 10;//满足条件的数字个数
}
int T;
scanf("%d", &T);
while(T--)
{
cin >> N;
N++;//将N加一
bool flag = false;
long long int ans = 0;
int len = 0;
memset(number,0,sizeof(number));
while(N != 0)
{
number[++ len ] = N%10;
N /= 10;
}
number[len + 1] = 0;//将高一位表示为0
for(int i = len; i > 0; i--)
{
ans += number[i] * dp[i-1][2];
if(flag)
ans += dp[i-1][0] * number[i];
if(number[i] > 4 && !flag)
ans += dp[i-1][1];
if(number[i+1] == 4 && number[i] == 9)
flag = true;
}
cout << ans <<endl;
}
return 0;
}