数位DP专题总结

简介

数位DP是一类非常精巧的动态规划,它解决的问题是 (0,r] 区间上满足某种性质的数的计数问题。例如,求 (0,468] 中数位中没有” 49 ”的数的个数。动态规划的关键是找到重叠子问题和最优子结构,为此数位DP将区间的右端点 r 看作是字符串,例如 468 被当做”468”处理。显然 468 是个 3 位数,比这个数小的数有,

000001 099
100101 199
200201 299
300301 399
400401 469

显然,如果我们能够逐段求出答案就可以找到最终答案。如何巧妙地组织这个解空间,然后逐步求解呢?
可以按照下图组织:
《数位DP专题总结》
上图中存在比较多的重叠子问题,可以使用动态规划来解决;需要特别强调的是如果百位为4或者百位为4且十位为6的时候,其对应的子问题,不具有通用性,不应该备忘。上述条件被称为limit条件。

引入

请计算 [0,234] 中不包含 49 的数字的个数。
如何求解这个问题呢?
首先看百位上的情况,百位一定是受限制的,其数值只能取0到4,而不是0到9。我们来考虑百位为0、1的情况,只需计算000到099中不包行49的数的个数就好了;当考虑百位为2时,情况变得复杂起来,其十位只能考虑0、1、2、3,而非0到9。百位为0、1不满足limit条件,其对应的子问题(00到99中不含49的数)有一定的通用性需要记录,以便复用。

上代码

#include<map>
#include<vector>
#include<unordered_set>
#include<unordered_map>
#include<list>
#include<queue>
#include<deque>
#include<stack>
#include<iostream>
#include<set>
using namespace std;
const int MAX_LEN = sizeof(int) / sizeof(char) * 8;
class Solution {
    public:
        Solution(int x) {
            this->x = x;
            memset(dp, (char)(-1), sizeof(dp));
        }
        int work() {
            string d = this->init();
            return this->dfs((int)d.size() - 1, d, true, 0);
        }
    private:
        string init() {
            string d;
            int x = this->x;
            while (x) {
                int tmp = x % 10;
                x /= 10;
                d.push_back(tmp);
            }
            return d;
        }
        int dfs(int pos, string& d, bool limit, int state) {
            if (pos == -1) {
                if (state == 49) {
                    return 1;
                } else {
                    return 0;
                }
            }
            if (!limit && dp[pos][state] != -1) return dp[pos][state];
        int maxNum = limit ? d[pos] : 9;
        int cnt = 0;
        for (int dNum = 0; dNum <= maxNum; ++dNum) {
            if (state == 4 && dNum == 9) {
                cnt += dfs(pos - 1, d, limit&&d[pos]==dNum, 49);
            } else if (state == 49) {
                cnt += dfs(pos - 1, d, limit&&d[pos]==dNum, 49);
            } else {
                cnt += dfs(pos - 1, d, limit&&d[pos]==dNum, dNum == 4 ? 4 : 0);
            }
        }
        if (!limit) {
            dp[pos][state] = cnt;
        }

        return cnt;
    }

    int x;
    int dp[MAX_LEN][2];
};

int main() {
    int x;
    cin>>x;
    Solution s(x);
    cout<<"result:\t";
    cout<<s.work()<<endl;
    return 0;
}

点赞