HDU 3555 Bomb Bomb

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 11470    Accepted Submission(s): 4086


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?
 

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.
 

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 15
Hint
From 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.
 
第一次接触数位DP题目,不太明白到底什么是数位DP,看了看题解,好像有点感觉。

题目意思是给定一段区间,求数字中含有49的数的个数。

首先显然需要将这个数按位存储在

建立数组dp[25][3],dp[i][0]表示到第i个位置的没有49的个数

dp[i][1]表示到第i个位置的没有49但是最高位是9的数的个数,像9,90,91,92,93,94,95,96,97,98,99这些都是。之所以统计这一项是因为只要在下一位添加4,就可以组成这些数个49

dp[i][2]表示到第i个位置的含49的数的个数

那么经过预处理以后,我们就可以得到一个位数这三项的个数,然后我们根据所给的数字就可求出对应的49的个数,方法如下:

我们仍从低位向高位进行分析,首先ans加上到第i位含49的数的个数,然后进行分析如果这个数当前位数大于4的话,加上所有该位第一位的dp[i][1]的数目乘以该数,这说明在当前位数的数量级下有的49的个数(因为高位大于了4),最后如果出现49含有的话,每一次除了加上上一位时所有含49的,还需要加上所有不是49的数的个数,因为有了49,高位无论加上什么数他都是含有49的。

代码如下,很好理解:flag表示是否出现了49

/*************************************************************************
	> File Name: Bomb.cpp
	> Author: Zhanghaoran
	> Mail: chilumanxi@xiyoulinux.org
	> Created Time: Tue 03 Nov 2015 06:01:49 PM CST
 ************************************************************************/

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>

using namespace std;

int num[25];
long long dp[25][3];
int T;
long long x;
void init(){
    dp[0][0] = 1;
    dp[0][1] = 0;
    dp[0][2] = 0;
    for(int 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];
    }
}


int make_num(long long x){
    int len = 0;
    while(x){
        num[++ len] = x % 10;
        x /= 10;
    }
    num[len + 1] = 0;
    return len;
}

int main(void){
    scanf("%d", &T);
    init();
    while(T --){
        cin >> x;
        int len = make_num(x);
        bool flag = false;
        long long ans = 0;
        for(int i = len; i >= 1; i --){  //1449 -> 9441
            ans += num[i] * dp[i - 1][2];
            if(flag)
                ans += num[i] * dp[i - 1][0];
            else{
                if(num[i] > 4){
                    ans += dp[i - 1][1];
                }
            }
            if(num[i + 1] == 4 && num[i] == 9)
                flag = true;
        }
        if(flag){
            ans ++;
        }
        cout << ans << endl;
    }
}
有做Beautiful numbers那个题,很自然的想到是否用DFS记忆搜索dp可以做。显然也是没有问题的。

加两个判断条件,一个是判断是否到达上限,一个判断最高位是否是4

代码:

/*************************************************************************
	> File Name: Bomb_dfs.cpp
	> Author: Zhanghaoran
	> Mail: chilumanxi@xiyoulinux.org
	> Created Time: Tue 10 Nov 2015 07:43:05 PM CST
 ************************************************************************/

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
using namespace std;

int T;
long long N;
long long dp[25][2];
int num[25];

long long work(int pos, bool fir_4, bool max){
    if(pos == 0){
        return 0;
    }
    if(!max && dp[pos][fir_4]){
        return dp[pos][fir_4];
    }

    long long ans = 0;
    int flag = max ? num[pos] : 9;
    for(int i = 0; i <= flag; i ++){
        if(fir_4 && i == 9){
            long long di = pow(10, pos - 1);
            ans += max ? N % di + 1 : di;
        }
        else{
            ans += work(pos - 1, i == 4, max && num[pos] == i);
        }
    }

    if(!max)
        dp[pos][fir_4] = ans;
    return ans;
}

long long calc(long long N){
    int i  = 1;
    while(N){
        num[i ++] = N % 10;
        N /= 10;
    }
    return work(i - 1, false, true);
}

int main(void){
    scanf("%d", &T);
    while(T --){
        scanf("%lld", &N);
        cout << calc(N) << endl;
    }
    return 0;
}