1. 程式人生 > >數位DP專題總結

數位DP專題總結

簡介

數位DP是一類非常精巧的動態規劃,它解決的問題是(0,r]區間上滿足某種性質的數的計數問題。例如,求(0,468]中數位中沒有”49”的數的個數。動態規劃的關鍵是找到重疊子問題和最優子結構,為此數位DP將區間的右端點r看作是字串,例如468被當做”468”處理。顯然468是個3位數,比這個數小的數有,

000 001 099
100 101 199
200 201 299
300 301 399
400 401 469

顯然,如果我們能夠逐段求出答案就可以找到最終答案。如何巧妙地組織這個解空間,然後逐步求解呢?
可以按照下圖組織:
這裡寫圖片描述


上圖中存在比較多的重疊子問題,可以使用動態規劃來解決;需要特別強調的是如果百位為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;
}