HDU 3555 Bomb 數位DP

一:題意 
給定一個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;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章