zoukankan      html  css  js  c++  java
  • HDU3555 Bomb 题解 数位DP

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555
    题目大意:求 \([1,n]\) 范围内有多少数包含“49”。
    解题思路:
    这个问题我们可以分两种解法来考虑:第一种是求不包含“49”的数的数量,用后减一下;另一种就是直接求包含“49”的数的数量。

    解法1:求多少数不包含“49”

    这种方法我们先通过数位DP求出 \([0,n]\) 区间范围内有多少数不包含“49”(假设数量为 \(x\) ),然后可以得到答案为 \(n+1-x\)

    我们可以设计一个函数 dfs(int pos, int stat, bool limit) 来返回区间 \([0,n]\) 范围内有多少数不包含“49”,其中:

    • pos 表示当前所处数位;
    • stat 表示前一位的数是不是‘4’(如果前一位是‘4’当前位是‘9’则凑成“49”);
    • limit 用于标记当前是否受限制(true:受限制;false:不受限制)。

    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    long long f[66][2], n;
    int T, a[66];
    void init() {
        memset(f, -1, sizeof(f));
    }
    long long dfs(int pos, int stat, bool limit) {
        if (pos < 0) return 1;
        if (!limit && f[pos][stat] != -1) return f[pos][stat];
        int up = limit ? a[pos] : 9;
        long long tmp = 0;
        for (int i = 0; i <= up; i ++) {
            if (stat && i == 9) continue;
            tmp += dfs(pos-1, i==4, limit && i == up);
        }
        if (!limit) f[pos][stat] = tmp;
        return tmp;
    }
    long long get_num(long long x) {
        int pos = 0;
        while (x) {
            a[pos++] = x % 10;
            x /= 10;
        }
        return dfs(pos-1, 0, true);
    }
    int main() {
        init();
        scanf("%d", &T);
        while (T --) {
            scanf("%lld", &n);
            printf("%lld\n", n+1-get_num(n));
        }
        return 0;
    }
    

    解法2:求多少数包含“49”

    相对解法1是间接的方式进行求解。我们现在这种方式则是 直接 求解 \([0,n]\) 范围内有多少个数包含“49”。

    我们可以设计一个函数 dfs(int pos, int stat, bool limit) 来返回区间 \([0,n]\) 范围内有多少数不包含“49”,其中:

    • pos 表示当前所处数位;
    • stat 表示前一位的数是不是‘4’(如果前一位是‘4’当前位是‘9’则凑成“49”);
    • limit 用于标记当前是否受限制(true:受限制;false:不受限制)。

    虽然是一样的,但是在处理的时候统计结果的方式却不一样。

    这里特别需要注意的是限制条件和非限制条件下不同的处理。

    假设我们现在要找的是区间 \([0,n]\) 范围内有多少数包含“49” 遍历到 pos 位并且 pos+1 位是‘4’, pos 位是‘9’,则我们可以得知:

    1. 如果当前处于限制状态下,则之后有 \(n \% 10^{pos} + 1\) 个数满足条件;
    2. 如果当前处于非限制状态下,则之后有 \(10^{pos}\) 个数满足条件。

    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    long long f[66][2], n;
    int T, a[66];
    void init() {
        memset(f, -1, sizeof(f));
    }
    long long pow10(int a) {
        if (a == 0) return 1;
        if (a == 1) return 10;
        long long t = pow10(a/2);
        return t * t * (a%2 ? 10 : 1);
    }
    long long dfs(int pos, int stat, bool limit) {
        if (pos < 0) return 0;  // 返回0说明没找到包含49的
        if (!limit && f[pos][stat] != -1) return f[pos][stat];
        int up = limit ? a[pos] : 9;
        long long tmp = 0;
        for (int i = 0; i <= up; i ++) {
            if (stat && i == 9) {   // 说明接下来pos位的10的pos次方个数都满足条件
                // tmp += pow10(pos);   // 直接这么写是错的,此时也要考虑边界条件限制
                // 修正如下
                if (limit) tmp += n % pow10(pos) + 1;
                else tmp += pow10(pos);
            }
            else tmp += dfs(pos-1, i==4, limit && i == up);
        }
        if (!limit) f[pos][stat] = tmp;
        return tmp;
    }
    long long get_num(long long x) {
        int pos = 0;
        while (x) {
            a[pos++] = x % 10;
            x /= 10;
        }
        return dfs(pos-1, 0, true);
    }
    int main() {
        init();
        scanf("%d", &T);
        while (T --) {
            scanf("%lld", &n);
            printf("%lld\n", get_num(n));
        }
        return 0;
    }
    
  • 相关阅读:
    基于typora编写Markdown文档
    VMware Workstation常见的故障处理
    VMware Workstation产品常用的快捷键
    2
    1
    9
    8
    7
    6
    5
  • 原文地址:https://www.cnblogs.com/quanjun/p/11964644.html
Copyright © 2011-2022 走看看