1. 程式人生 > >數位DP——HDU 3555 要49

數位DP——HDU 3555 要49

題目連結:

題目大意:

給定一個數n,求取1-n這個閉區間中包含49的數字的個數。(n小於2的64次方)

解題思路:

由於這個題目的範圍比較大,所以採用暴力肯定會超時,而且不能開除這麼大的陣列。

比較基礎的數位DP。

定義了3種狀態:

dp[i][0]:i位數字的時候不包含49的數字個數

dp[i][1]:i位數字的時候不包含49並且最高位是9的數字個數

dp[i][2]:i位數字的時候包含49的數字個數

原始碼:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int date[25];
__int64 dp[25][3];
//狀態0表示不包含49
//狀態1表示不包含49並且最高位是9
//狀態2表示包含49
void init()     //預處理dp陣列,注意這個陣列開大點,到25左右,不然會wa
{
    int i;
    memset(dp,0,sizeof(dp));
    dp[0][0]=1;
    for(i=1;i<=25;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];
    }
    return;
}
__int64 DP(__int64 x)
{
    __int64 ans,sum,flag;
    int len,i;
    ans=len=flag=0;
    while(x)
    {
        date[++len]=x%10;
        x/=10;
    }
    date[len+1]=0;
    for(i=len;i>=1;i--)
    {
        ans+=dp[i-1][2]*date[i];        //後面出現了49,當前位可以取0-date[i]-1
        if(flag)
            ans+=dp[i-1][0]*date[i];    //前面出現了49,當前位可以去0-date[i]-1
        else
        {
            if(date[i]>4)
                ans+=dp[i-1][1];        //當前位取4,下一位取9
        }
        if(date[i]==9 && date[i+1]==4)
            flag=1;
    }
    return ans;
}
int main()
{
	//freopen("in.txt","r",stdin);
	int cs;
	__int64 n;
	init();
	scanf("%d",&cs);
	while(cs--)
	{
	    scanf("%I64d",&n);
	    printf("%I64d\n",DP(n+1));
	}
	return 0;
}