Problem Description
The counter-terrorists found a time bomb in the dust. But this time the terrorists improve on the time bomb. The number sequence of the time bomb counts from 1 to N. If the current number sequence includes the sub-sequence "49", the power of the blast would add one point.
Now the counter-terrorist knows the number N. They want to know the final points of the power. Can you help them?
Now the counter-terrorist knows the number N. They want to know the final points of the power. Can you help them?
Input
The first line of input consists of an integer T (1 <= T <= 10000), indicating the number of test cases. For each test case, there will be an integer N (1 <= N <= 2^63-1) as the description.
The input terminates by end of file marker.
The input terminates by end of file marker.
Output
For each test case, output an integer indicating the final points of the power.
Sample Input
3 1 50 500
Sample Output
0 1 15HintFrom 1 to 500, the numbers that include the sub-sequence "49" are "49","149","249","349","449","490","491","492","493","494","495","496","497","498","499", so the answer is 15.
改走递归路线!
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
#include<stack>
using namespace std;
typedef long long ll;
ll dp[22][3];
/*
dp[i][0]:前i位不含不吉利数的个数。
dp[i][1]:前i位不含不吉利数且i+1位是4的个数。
dp[i][2]:前i位含不吉利数的个数。
*/
int digit[22];
ll dfs(int pos,int st,bool limit)
{
if(pos==0)return st==2;
if(!limit&&dp[pos][st]!=-1)return dp[pos][st];
ll ans=0;
int end=limit?digit[pos]:9;
for(int i=0; i<=end; i++)
if((st==1&&i==9)||st==2)
ans+=dfs(pos-1,2,limit&&(i==end));
else if(i==4)
ans+=dfs(pos-1,1,limit&&(i==end));
else ans+=dfs(pos-1,0,limit&&(i==end));
if(!limit)
dp[pos][st]=ans;
return ans;
}
ll get(ll x)
{
int bj=0;
while(x)
digit[++bj]=x%10,x/=10;
return dfs(bj,0,1);
}
int main()
{
int t;
memset(dp,-1,sizeof(dp));
scanf("%d",&t);
while(t--)
{
ll n;
scanf("%I64d",&n);
printf("%I64d\n",get(n));
}
return 0;
}
初学时的递推:
数位dp基础题,和网上的方法不大一样,用dp[i][j]记录以j开头的i位数里面含有49的有多少个。
此题和那个杭电的不要‘62’dp记录表示的正好相反,本来还以为计算方法一样的,结果代码一敲出来答案全是0。。。
所以自己又仔细思考了一下,这个dp记录方式如果相反的话,得到dp值是需要我们思考的!
typedef long long ll;
ll dp[25][25];
void init()
{
memset(dp,0,sizeof(dp));
ll hh=1;
for(int i=2;i<=18;i++)
{
for(int j=0;j<10;j++)
for(int k=0;k<10;k++)
{
if(j==4&&k==9)
dp[i][j]+=hh;
else
dp[i][j]+=dp[i-1][k];
}
hh*=10;
}
}
如果理解dp值的含义并自己推到一下其实我的代码很好理解的。
再就是最后的处理了。dp的最终处理绝对比算出dp值要难。。。。
将数字分解为每一位,从高位到低位枚举,不过,需要注意的是若遇到494949这个数字,我们枚举的步骤也得要理解!首先ans+=dp[6][0~3],然后ans+=[5][0~8],接着!看到49了!也就意味着49****后面4个数字怎么变都会含有49,那么我们在此直接ans+=4950!(因为从0到4949有4950个数)
?然后怎么办呢?继续?,这就是我说的要理解枚举的步骤,我们首先分别枚举了以0,1,2,3开头的6位数含有49的个数,剩下400000到494949含有49的个数,然后我们枚举了400000到489999的含有49的个数,剩下490000到494949含有49的个数,接着,我们就直接加上了4950,因为490000到494949所有的数字都有49,直接加上就ok!然后我们不就枚举完毕了吗?!!我们还需要继续往下用ans去加dp[0~4]的值吗??我觉得已经说得比较明了了吧!所以枚举这个过程一定要自己理解!
#include <iostream>
#include <stdio.h>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long ll;
ll dp[25][25];
void init()
{
memset(dp,0,sizeof(dp));
ll hh=1;
for(int i=2;i<=18;i++)
{
for(int j=0;j<10;j++)
for(int k=0;k<10;k++)
{
if(j==4&&k==9)
dp[i][j]+=hh;
else
dp[i][j]+=dp[i-1][k];
}
hh*=10;
}
}
int main()
{
init();
int t;
//for(int i=1;i<=18;i++){for(int j=0;j<10;j++)cout<<dp[i][j]<<' ';cout<<endl;}
scanf("%d",&t);
ll n;
while(t--)
{
int digit[25]={0},bj=0;
scanf("%lld",&n);ll nn=n,ans=0;
while(n)
{digit[++bj]=n%10;n/=10;}
bool flag=0;
for(int i=bj;i>0;i--)
{
for(int j=0;j<digit[i];j++)
ans+=dp[i][j];
if(flag==1)
break;
if(digit[i]==4&&digit[i-1]==9&&flag==0)
{
ll tem=0;
if(bj>2)
{
for(int mm=i-2;mm>0;mm--)
tem*=10,tem+=digit[mm];
ans+=tem+1;
}else
ans++;
flag=1;
}
}
cout<<ans<<endl;
}
return 0;
}