hdu 3555 Bomb(数位dp)

题意:求1到n有多少个数中含有49,1<=n<=2^63-1(2^32是10位,2^64约20位)

思路:数位dp

dp[i][0],长度为i,不含有49的个数
dp[i][1],长度为i,不含有49,最高位为9的个数
dp[i][2],长度为i,含有49的个数

状态转移方程:

dp[i][0]=10*dp[i-1][0]-dp[i-1][1];//不含49的前面加0~9,减掉9前面加4
dp[i][1]=dp[i-1][0];//不含49的前面加9
dp[i][2]=10*dp[i-1][2]+dp[i-1][1];//含49的前面加0~9,加上9前面加4

求解:从最高位开始遍历,每一位求的都是  前缀+小于当前位  的符合条件的个数。

1.首先加上当前位置之后包含49的个数,因为当前为可以填0~bit[i]-1,所以乘以bit[i],ans+=dp[i-1][2]*bit[i];//注意这个位置,求的是前缀+当前位置
2.前面的前缀出现49了,ans+=dp[i-1][0]*bit[i];//
3.前缀没有出现49,并且当前位>4,ans+=dp[i-1][1];

#include<iostream>
#include<stdio.h>
using namespace std;

long long dp[25][3];
/*
dp[i][0],不含有49
dp[i][1],不含有49,最高位为9
dp[i][2],含有49
*/
void init(){
    dp[0][0]=1;
    dp[0][1]=dp[0][2]=0;
    int i;
    for(i=1;i<25;++i){
        dp[i][0]=10*dp[i-1][0]-dp[i-1][1];//不含49的前面加0~9,减掉9前面加4
        dp[i][1]=dp[i-1][0];//不含49的前面加9
        dp[i][2]=10*dp[i-1][2]+dp[i-1][1];//含49的前面加0~9,加上9前面加4
    }
}
int bit[25];
long long calc(long long n){
    int len=0,i;
    while(n){
        bit[++len]=n%10;
        n/=10;
    }
    bit[len+1]=0;
    bool flag=false;
    long long ans=0;
    for(i=len;i>=1;--i){
        ans+=dp[i-1][2]*bit[i];//注意这个位置,求的是前缀+当前位置
        if(flag)ans+=dp[i-1][0]*bit[i];
        else if(bit[i]>4)ans+=dp[i-1][1];
        if(bit[i+1]==4&&bit[i]==9)flag=true;
    }
    if(flag)++ans;//加上n本身
    return ans;
}

int main(){
    init();
    int t;
    long long n;
    scanf("%d",&t);
    while(t--){
        scanf("%lld",&n);
        printf("%I64d\n",calc(n));
    }
    return 0;
}
View Code

 

posted @ 2015-08-25 12:56  gongpixin  阅读(280)  评论(0编辑  收藏  举报