HDU-3555-Bomb

这题就是数位dp的一道题目,我们首先把一个264范围内的数,也就是1018范围之内的数,给分解开来。
分解之后,我们深搜它,深搜函数的参数分别是,长度、是不是4、是不是上界(也就是是不是我们搜索的这个数是不是当前搜索位的最大值)。
对于深搜函数,我们的搜索边界就是,如果此时就剩下一位待搜索,也就是我们搜到了digit[0]的位置,我们就返回一个1值,此时条件即为len==0。
抑或我们搜索的过程中,这一位不是上界,而是小于上界的其他数,并且我们的dp数组更新过,里面不是零,我们就返回之前的值,也就是dp[len][is4]它的值。
它的值代表什么意思呢?它的意思就是说,我们之前搜索到len=2的时候,这一位不是4,然后我们对于它的个位搜索得到一个10,因为如果十位不是4的话,那这十个数里面肯定不会有49,所以dp[2][0]也就等于了10。
而当我们从其它的分支进来的时候,我们发下,此时len相同,并且这一位也不是4,我们不需要再进行搜索了,我们有之前存下的值。
这也就相当于是记忆化搜索了,相当于是一个非常优秀的剪枝。
对于相同的位数,如果是4,我们之前还没存下的话,我们就继续搜索,搜得不是上界之时的dp值,然后供其他的分支使用即可。
这是从深搜的角度解释记忆化搜索的,下面放一个视频解释,是另一个角度的,讲的还挺不错的。
https://www.bilibili.com/video/av27156563?from=search&seid=2518917738675420605

#include <iostream>
#include <cstring>
using namespace std;
int digit[20];
long long dp[20][2];

long long dfs(int len,bool is4,bool limit)
{
    if (len==0)
        return 1;
    if (!limit&&dp[len][is4])
        return dp[len][is4];
    long long cnt = 0, up_bound = (limit ? digit[len] : 9); 
    for (int i = 0; i <= up_bound;i++) {
        if (is4&&i==9)
            continue;
        cnt += dfs(len - 1, i == 4, limit && i == up_bound);
    }
    if (!limit)
        dp[len][is4] = cnt;
    return cnt;
}

long long solve(long long num)
{
    int k = 0;
    while (num) {
        digit[++k] = num % 10;
        num /= 10;
    }
    return dfs(k, false, true);
}

int main()
{
    long long t, n;
    cin >> t;
    while (t--) {
        cin >> n;
        memset(dp, 0, sizeof(dp));
        cout << n + 1 - solve(n) << endl;
    }
    return 0;
}
posted @ 2019-02-09 17:00  xyee  阅读(218)  评论(0编辑  收藏  举报