デジタル DP アルゴリズム学習の概要

1.デジタルDP+テンプレートの簡単な説明

デジタル dp は、数を数えるために使用される動的計画アルゴリズムです。一般に、 [左、右]間隔で指定された条件を満たす数値の数を数える必要があります。たとえば、HDU 2089 には、指定された条件に 4 と 62 は含まれません。間隔 62 個の数字の数、桁数 dp は、実際には、時間計算量を最適化するために、いくつかの数字をフィルタリングし、いくつかのデータを記憶する (後述) ことによって、暴力的な列挙アルゴリズムを最適化したものです。

まず、dfs ( int step , int state , bool lead , bool limit ) 関数には 4 つのパラメーターがあります。

  • step: 現在列挙されている桁数 (1 ~ 100 など) を示します。最初に 100 桁の数字を列挙し、次に 10 桁の数字を列挙し、最後に 1 桁の数字を列挙します。

  • state: 状態条件。暗記に使用されます。たとえば、[1,9299] の間にある 4 を含まない数値の数を数えたい場合、列挙が次の値に達したら、まず 1 から列挙を開始する必要があるのは当然です。 1000 3 桁しかない、千の数を列挙するとき、千の桁が 1 のとき、次の 3 桁は次のとおりであることがすでにわかっています。 4 を含む数字の数について、千の位が 2 になる場合、千の位 1 と 2 には 4 が含まれていないため、結果を直接返すことができます。その後、千の位 1 と千の位 2 の後の 3 桁が返されます。計算の繰り返しを避けるために配列を記憶できるように、一致させる方法は同じでなければなりません。

  • lead: 先頭のゼロ、問題によっては 00xxx などの先頭のゼロも結果に影響するため、ブール変数の lead を設定する必要があります。もちろん、問題によっては先頭のゼロが結果に影響しないため、使用できません

  • limit: 制限マーク。すべての数値を列挙したいです。これらの数値には上限が必要です。たとえば、上記では [1,9299] の間のデータを列挙したいとします。千の位が 9 の場合、明らかに百の位は最高値は 2 までのみ列挙できます。それ以外の場合は列挙の上限を超えますが、千の位が 8 の場合、次の 3 桁が何であっても、千の位は 8 であるため、現時点では上限はありません。 9299 の範囲を超えません

次に、solve ( int x ) の機能は、dfs を走査するために数値の各桁を取り出すことです。

以下はデジタル dp のテンプレートです。

#include<iostream>
using namespace std;
int a[20]; //存储数字位数
int dp[20][2]; //记忆化数组

int dfs(int step, int state, bool lead, bool limit) {
    if (step == -1) return 1; //当遍历到超出数字位数时,说明此情况可行,返回1
    if (!limit && !lead && dp[step][state] != -1) //记忆化,节省重复计算
        return dp[step][state];
    int up = limit ? v[step] : 9;  //取得上界
    int ans = 0; //开始计数
    for (int i = 0; i <= up; i++) {
        if (条件1) ...
        if (条件2) ...
        ans += dfs(step - 1, /*状态转移*/, lead && i == 0, limit && i == v[step]);
    }
    if (!limit && !lead) //在一定条件下记录结果,对应上面记忆化
        dp[step][state] = ans;
    return ans;
}
int solve(int x) {
    int pos = 0;
    while (x) { //取出各个位的数字,存在a中
        a[pos++] = x % 10;
        x /= 10;
    }
    memset(dp, -1, sizeof(dp)); //初始化dp数组值为-1
    return dfs(pos - 1, /*状态*/, true, true); //刚开始比最高位还高的数字为0,所以有前导零,并且有限制
}
int main() {
    int le, ri;
    while (cin >> le >> ri) {
        int ans = solve(ri) - solve(le - 1);
        cout << ans << endl;
    }
    return 0;
}

2. HDU 2089 は 62桁の dp ソリューションを必要としません

状態変数は最も制限された条件で記憶される必要があります。たとえば、この質問では 62 という制限はなく、4 という制限もありません。ビットは 1 つしかないため、状態はビットが 6 であるかどうかでマークされます。 [1,100]の解が条件を満たしているとします。状態をis_fourと表すと、10の位は5、10の位は6となり、5も10の位も6ではないので、条件を満たす数字は同じになります。 6は4ですが、十の位が6の場合、一の位は2にはなりません(これはタイトルで要求されています)ので、データの不整合が発生し、状態変数は最も制限的な方法で記憶する必要があると結論付けられます。条件。

#include<iostream>
#include<vector>
using namespace std;
int a[20];
int dp[20][2];

int dfs(int step, int pre, bool is_six, bool limit) { 
    //pre为前一位数字,用于判断是否存在62,此处is_six就是模板中的state,表示当此位为6和不为6时,后续的结果不同
    if (step == -1) return 1;
    if (!limit && dp[step][is_six] != -1) 
        return dp[step][is_six];
    int up = limit ? a[step] : 9;
    int ans = 0;
    for (int i = 0; i <= up; i++){
        if (i == 4) continue; //存在4则跳过
        if (i == 2 && pre == 6) continue; //存在62则跳过
        ans += dfs(step - 1, i, i == 6, limit && i == a[step]);
    }
    if (!limit) 
        dp[step][is_six] = ans;
    return ans;
}
int solve(int x) {
    int pos = 0;
    while (x != 0) {
        a[pos++] = x % 10;
        x /= 10;
    }
    memset(dp, -1, sizeof(dp)); //初始化dp数组值为-1
    return dfs(pos - 1, -1, 0, true);
}
int main() {
    int n, m;
    while (cin >> n >> m && n != 0 && m != 0) {
        int ans = solve(m) - solve(n - 1);
        cout << ans << endl;
    }
    return 0;
}

おすすめ

転載: blog.csdn.net/qq_21891843/article/details/129767659