給定一個N,要求你求出[0,N]內所有含有49的數字個數,其中N (1 <= N <= 2^63-1)。
二:解析
1,對於這一類數位dp,需要掃描[0,N]區間所有數的每一位,爲了避免對每一個數取出每位數。我們利用數組來存儲區間[0,N]的每一個數,數組的長度爲N的位數 x (即最大位數) ,對於沒有x位的數來說,我們在前位數面補0即可,這樣我們只要掃描該最長數組,枚舉數組每一位可能取的數(0,1,,,,9),就可以掃描區間[0,N]所有數字。
2,dp數組含義
dp[len][0] 表示前驅爲非4,長度爲len裏面 “49” 數的個數。
dp[len][1] 表示前驅爲4,長度爲len裏面 “49” 數的個數。
注意:
這些記錄都是在沒最大數限制的情況下記錄的,所以在調用時必須不受大數限制(即前驅不是最大值)。這是因爲我們每次輸入的最大值是不同的,所以受最大值限制的數也會不同(即沒有通用性),只要沒受最大數限制的那些數對所有的N都可用(即具有通用性)。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
typedef long long LL;
LL dp[30][2];
//dp[len][0] 長度爲len,且前驅不是4的個數
LL total[30];
int bit[30];
LL T,N;
LL DFS(int Len,bool four,bool Max)
{//長度,前驅是不是4, 前面是不是最大值
if(Len==0)
return 0;
if(!Max&&(dp[Len][four]!=-1))
return dp[Len][four];//不能是最大值時不受限制
LL res=0;
int bian=9;
if(Max)//len層循環的邊界
bian=bit[Len];
for(int i=0;i<=bian ;i++)
{//從0開始所以可以遍歷所有0——N數
if(four&&i==9)
{//出現49就剪枝
if(Max)//前驅全是最大值的情況
res+=(N%total[Len-1])+1;
else
res+=total[Len-1];
}
else
res+=DFS(Len-1,i==4,Max&&(i==bit[Len]));
}
if(!Max)//記錄非最大值下一個N再用
dp[Len][four]=res;
return res;
}
void solv(LL M)
{
int len=1;
while(M)
{
bit[len++]=M%10;
M=M/10;
}
bit[len]=0;//所以[0,N]的數第len爲都爲0
cout<<DFS(len-1,false,true)<<endl;
}
int main()
{
total[0]=1;
for(int i=1;i<30;i++)
total[i]=total[i-1]*10;
memset(dp,-1,sizeof(dp));
scanf("%d",&T);
while(T--)
{
scanf("%lld",&N);
solv(N);
}
return 0;
}