[動的プログラミング] 単純なマルチステート

動的プログラミング (単純なマルチステート)

1. マッサージ師

トピックリンク

  1. ステータス表示

    dp[i]位置 i に到着したときの予定の最長時間を示します。しかし、私たちはこの質問に答えるかどうかを選択できます。したがって、さらに 2 つのサブ状態に分けることができます。

    • f[i]表示: i 位置に到達するまでに許容される最大時間
    • g[i]示す: i 位置に到達したときに受け入れられない最大継続時間
  2. 状態遷移方程式

    2vu4cjw7da-1690362953455.png

  3. 初期化

    このトピックは比較的単純であるため、仮想ノードの方法を使用する必要はなく、後でフォームを入力するときに境界を越えないように初期化されます。

    f[0] = nums[0], g[0] = 0

  4. フォームに記入

    左から右へ

  5. 戻り値

    最後の値を受け入れるか、最大値を受け入れません

ACコード:

class Solution 
{
    
    
public:
    int massage(vector<int>& nums) 
    {
    
    
        int n = nums.size();
        if (n == 0) return 0;
        vector<int> f(n);
        auto g = f;
        f[0] = nums[0], g[0] = 0;
        for (int i = 1; i < n; i++)
        {
    
    
            f[i] = g[i - 1] + nums[i];
            g[i] = max(f[i - 1], g[i - 1]);
        }
        return max(f[n - 1], g[n - 1]);
    }
};

2.強盗||

トピックリンク

分析します:

部屋は連続している、つまりリングであるため、分類して議論することができます。

  • 1番目を盗んだ場合、2番目と最後は盗めません
  • 最初のものを盗むのではなく、2番目と最後のものを盗むことができます

したがって、2 つの場合の最大値のみが必要です。

  1. ステータス表示

    dp[i]盗まれた場合の上限額を示していますが、それでも盗む場合と盗まない場合の2つに分けられます。

    f[i]iを盗む際の最大量を示します。

    g[i]iを盗まない場合の上限を示します。

  2. 状態遷移方程式

    qx81yjpg3g-1690364303443.png

  3. 初期化

    後続のフォーム入力が境界を越えないようにする

  4. フォームに記入

    両方を左から右に入力してください

  5. 戻り値

    最大値

ACコード:

class Solution 
{
    
    
public:
    int rob(vector<int>& nums) 
    {
    
    
        int x = 0, y = 0;
        int n = nums.size();
        x += nums[0];
        x += recursion(2, n - 2, nums);
        y += recursion(1, n - 1, nums);
        return max(x, y);
    }
    int recursion(int left, int right, vector<int> &v)
    {
    
    
        if (left > right) return 0;
        int n = v.size();
        vector<int> f(n);
        auto g = f;
        f[left] = v[left]; // 初始化
        for (int i = left + 1; i <= right; i++)
        {
    
    
            f[i] = g[i - 1] + v[i];
            g[i] = max(g[i - 1], f[i - 1]);
        }
        return max(f[right], g[right]);
    }
};

3. 削除してポイントを獲得する

トピックリンク

分析: すべての数値のポイントの合計を配列に入れて、住宅強盗を実行します。

元の配列を新しい配列に変換します。新しい配列の添字iに対応する値はi、元の配列内の元の配列の要素の合計です。たとえば[2, 2, 3, 3, 3, 4]、元の配列は新しい配列に変換されます[0, 0, 4, 9, 4]新しい配列の添字は、0和1元の配列 に0和1これら 2 つの2数値が存在しないことを示し、新しい配列の添字の値は であり4、元の配列では2すべての合計が であることを示します4変換の目的は、新しい配列から削除して得られるポイントnums[i]、つまり奪える量を取得することです。nums[i]削除後は合計を削除する必要があるためnums[i] + 1nums[i] - 1新しい配列に隣接する要素を取り込むことができなくなり、隣接する要素を取り込めないことは強盗と同じになります。次に、家を強盗する方法を使用して問題を解決できます

ACコード:

class Solution 
{
    
    
public:
    const int N = 10001;
    int deleteAndEarn(vector<int>& nums) 
    {
    
    
        vector<int> arr(N);
        for (auto e : nums) arr[e] += e;
        vector<int> g(N);
        auto f = g;
        for (int i = 1; i < N; i++)
        {
    
    
            f[i] = g[i - 1] + arr[i];
            g[i] = max(g[i - 1], f[i - 1]);
        }
        return max(g[N - 1], f[N - 1]);
    }
};

4. 家にペンキを塗る

トピックリンク

  1. ステータス表示

    dp[i]iに到達する際に最低限必要なコストを示します。しかし、i に関しては 3 つの状況が考えられ、3 つのサブ状態に分割する必要があります。

    dp[i][0], dp[i][1], dp[i][2]

  2. 状態遷移方程式

    wrmwgl7sbc-1690365960472.png

  3. 初期化

    仮想ノードの使用

  4. フォームに記入

  5. 戻り値

    3 つのテーブルの最小値を返します

ACコード:

class Solution 
{
    
    
public:
    int minCost(vector<vector<int>>& costs) 
    {
    
    
        // 0: 红色 1:蓝色 2:绿色
        int n = costs.size();
        vector<vector<int>> dp(n + 1, vector<int>(3));
        for (int i = 1; i <= n; i++)
        {
    
    
            dp[i][0] = min(dp[i - 1][1], dp[i - 1][2]) + costs[i - 1][0];
            dp[i][1] = min(dp[i - 1][0], dp[i - 1][2]) + costs[i - 1][1];
            dp[i][2] = min(dp[i - 1][0], dp[i - 1][1]) + costs[i - 1][2];
        }
        int ret = INT_MAX;
        for (int i = 0; i < 3; i++)
        {
    
    
            ret = min(ret, dp[n][i]);
        }
        return ret;
    }
};

5. 株式の売買に最適な時期には凍結期間が含まれます

トピックリンク

  1. ステータス表示

    dp[i]位置 i に到達したときの最大利益を示しますが、位置 i に到達した時点でもまだ 3 つのサブステートが存在します。

    • dp[i][0]、その後購入状態であることを示します
    • dp[i][1]は、その後取引可能な状態になることを意味します。
    • dp[i][2]、その後、私がフリーズ状態にあることを示します
  2. 状態遷移方程式

    このような状態を相互に変換できる場合は、次の方法を使用して分析できます。

    bh40p1zbcb-1690367957450.png

  3. 初期化

    dp[0][0] = -prices[0], dp[0][1] = 0, dp[0][2] = 0

  4. フォームに記入

    3 つのフォームに同時に記入する

  5. 戻り値

    3 つの状態の最後の最大値を返します。

ACコード:

class Solution 
{
    
    
public:
    int maxProfit(vector<int>& prices) 
    {
    
    
        int n = prices.size();
        vector<vector<int>> dp(n, vector<int>(3));
        dp[0][0] = -prices[0];
        for (int i = 1; i < n; i++)
        {
    
    
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][2]);
            dp[i][2] = dp[i - 1][0] + prices[i];
        }
        return max(max(dp[n - 1][0], dp[n - 1][1]), dp[n - 1][2]);
    }
};

6. 株式の売買に最適な時期には手数料が含まれます

トピックリンク

  1. ステータス表示

    dp[i]i ポジションに到達したときの最大利益を示しますが、i ポジションに到達したときの状態は 2 つあります

    dp[i][0]:購入状態であることを示します。

    dp[i][1]販売状況を示します

  2. 状態遷移方程式

    ogyovzrz17-1690372796443.png

  3. 初期化

    初めは購入状態であればdp[0][0] = -prices[0]

  4. フォームに記入

  5. 戻り値

ACコード:

class Solution 
{
    
    
public:
    int maxProfit(vector<int>& prices, int fee) 
    {
    
    
        int n = prices.size();
        vector<vector<int>> dp(n, vector<int>(2));
        dp[0][0] = -prices[0];
        for (int i = 1; i < n; i++)
        {
    
    
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee);
        }
        return max(dp[n - 1][0], dp[n - 1][1]);
    }
};

7. 株を売買するのに最適な時期|||

トピックリンク

  1. ステータス表示

    dp[i]ポジション i への最大利益を示しますが、いくつかの状態にも分かれています

    f[i][j]i までが j 回目の購入の最大利益になることを示します

    g[i][j]i までが j 回目の購入の最大利益になることを示します

  2. 状態遷移方程式

    zrwwmb104n-1690374918443.png

  3. 初期化

    f[0][0] = -prices[0], g[0][0] = 0

  4. フォームに記入

    上から下、各行は左から右へ

  5. 戻り値

    売り状態の最後の数件の最大値

ACコード:

class Solution 
{
    
    
public:
    const int N = 0x3f3f3f3f;
    int maxProfit(vector<int>& prices) 
    {
    
    
        int n = prices.size();
        vector<vector<int>> f(n, vector<int>(3, -N));
        auto g = f;
        f[0][0] = -prices[0], g[0][0] = 0;
        for (int i = 1; i < n; i++)
        {
    
    
            for (int j = 0; j < 3; j++)
            {
    
    
                f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]);
                g[i][j] = g[i - 1][j];
                if (j - 1 >= 0) g[i][j] = max(g[i][j], f[i - 1][j - 1] + prices[i]);
            }
        }
        int ret = 0;
        for (int i = 0; i < 3; i++)
        {
    
    
            ret = max(ret, g[n - 1][i]);
        }
        return ret;
    }
};

8. 株を売買するのに最適な時期 IV

トピックリンク

  1. ステータス表示

    または 2 つのサブ状態

    f[i][j]ポジション i での購入状態での j 番目の株式購入の最大利益を示します

    g[i][j]ポジション i での売り状態での j 番目の株式購入の最大利益を示します

  2. 状態遷移方程式

    画像-20230726205442099

  3. 初期化

    f[0][0] = -prices[0], g[0][0] = 0

  4. フォームに記入

    上から下、左から右へ

  5. 戻り値

    すべての行の最大値を返します

ACコード:

class Solution {
    
    
public:
    const int N = 0x3f3f3f3f;
    int maxProfit(int k, vector<int>& prices) {
    
    
        int n = prices.size();
        vector<vector<int>> f(n, vector<int>(k + 1, -N));
        auto g = f;
        f[0][0] = -prices[0], g[0][0] = 0;
        for (int i = 1; i < n; i++)
        {
    
    
            for (int j = 0; j <= k; j++)
            {
    
    
                f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]);
                g[i][j] = g[i - 1][j];
                if (j - 1 >= 0) g[i][j] = max(g[i][j], f[i - 1][j - 1] + prices[i]);
            }
        }
        int ret = 0;
        for (int i = 0; i <= k; i++)
        {
    
    
            ret = max(ret, g[n - 1][i]);
        }
        return ret;
    }
};

おすすめ

転載: blog.csdn.net/qq_63474430/article/details/131947599