题目大意
给出一个数字N,求出从1到N的数中含有字串49的串的个数。
解题思路
我们可以按照递推的思想来构造,逐步求解,长度为i的串可以由长度为i-1的串在构造得到,由低位到高位构造求解
设置一个二维数组dp用来保存状态。dp[i][0]表示长度为i且不含有字串49的串的个数,dp[i][1]代表长度为i不含有字串49且以9开头的串的个数,dp[i][2]代表长度为i且含有字串49的串的个数。
可以列出状态转移方程为:
dp[i][0]=dp[i-1][0]*10-dp[i-1][1];
//在长度为i-1的串基础上,在首位前增加一位数字 0-9都满足条件,但是要减去一种情况:原串的首位为9且新增位数字为4。这里要注意dp[i][0]是包括dp[i][1]的,也就是说首位为9的不含49字串的数目也包含在dp[i][0]中。
dp[i][1]=dp[i-1][0];
//新增的以9开头的串可由原串基础上增加一位数字9得到。由于dp[i-1][0]中已经包含了以9开头的串,所以不需要再加上dp[i-1][1];
dp[i][2]=dp[i-1][2]*10+dp[i-1][1];
//长度为i且含49的串可以由长度为i-1且含49(dp[i-1][2]*10)前增加任意数字获得,也可以由原9开头的不含49串在加上首位4(dp[i-1][1])获得;
下面来讨论详细的过程:
1.将n的各位分别存入数组中
2.例如 47349
高位为0-3时,长度为4的串都能讨论到(0-39999 get)
高位为4时,下一位为0-6时,长度为3的串都能讨论到(40000-46999 get),这里要注意有一种特殊情况被忽略了,当高位的下一位大于4时,会有 449XX 的情况都满足题意但是却没有被讨论到。
高位为47时,下一位为0-2,长度为2的串都能讨论到(47000-47299 get),由于高位的下一位不大于4,故不存在 4749X 的情况。
高位为473时,下一位为0-3时,长度为1的串都能被讨论到(47300-47339 get)。但是由于有47349的存在,所以最终结果要加上这种情况。也可以通过对n+1的方式避免讨论n的最低两位为49的情况。
附上代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long int ll;
ll dp[25][3],t,num[1000];
ll n;
void init()
{
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][1]=dp[i-1][0];
dp[i][2]=dp[i-1][2]*10+dp[i-1][1];
}
}
void solve()
{
ll ans=0;
int cont=0,flag=0;
memset(num,0,sizeof(num));
n++;
while(n)
{
num[++cont]=n%10;
n/=10;
}
for(int i=cont;i>=1;i--)
{
ans+=num[i]*dp[i-1][2];
if(flag)
ans+=num[i]*dp[i-1][0];
else if(num[i]>4)
ans+=dp[i-1][1];
if(num[i+1]==4&&num[i]==9)
flag=1;
}
printf("%lld\n",ans);
}
int main()
{
init();
scanf("%lld",&t);
while(t--)
{
scanf("%lld",&n);
solve();
}
return 0;
}