動的計画法のストック問題-6つの問題を排除するための一般的な方法

目次

1つ:株式の一般的な枠組みの紹介

I.はじめに

2:徹底的なフレームワーク

3:状態移行フレームワーク

4.注意が必要な事項

V.まとめ

2:実際の戦闘トピック

トピック1:株式を売買するのに最適な時期(k = 1)

トピック2:株式を売買するのに最適な時期2(k =正の無限大)

トピック3:株式を売買するのに最適な時期3(k = 2)

トピック4:株式を売買するのに最適な時期3(k =任意の整数)

質問5:株を売買するのに最適な時期には凍結期間が含まれます

トピック6:株式を売買するのに最適な時期には手数料が含まれます


1つ:株式の一般的な枠組みの紹介

I.はじめに

株式の売買には共通の問題があります。まず、4番目の質問(最大トランザクション数をkに制限)の分析を1つずつ解決してみましょう。

4番目の質問(トランザクションの最大数をkに制限する)が最も一般化された形式であるため、他の質問はこの形式の簡略化です。

最初の質問:1つのトランザクションのみを実行します。これはk = 1に相当します。

質問2:無制限の数のトランザクション、k =正の無限大に相当

3番目の質問:2つのトランザクションのみを実行します。これはk = 2に相当します。

質問5および6:無制限の回数、6つのトランザクション凍結期間の追加条件、および手数料が追加されます。

2:徹底的なフレームワーク

株式発行については、再帰的思考を徹底的に使用するのではなく、状態を徹底的に使用します。

私たちは毎日行き、いくつかの可能な状態の合計を見て、次に各状態に対応する選択肢を見つけます。

すべての状態を列挙します。枯渇の目的は、対応する選択に従って状態を更新することです。

フレームワークコードのフローチャート:

for 状态1 in 状态1的所有取值:
    for 状态2  in  状态2的所有取值:
       for ...
          dp[状态1][状态2][...] = 择优(选择1,选择2,...)

株式発行の場合、毎日、買い、売り、操作なしの3つの選択肢があります。これらの3つの選択肢を表すために、買い、売り、休憩を使用します。

しかし、問題は、これらの3つのオプションを任意に選択できるのは毎日ではないということです。なぜなら、売りは買いの後になければならず、買いは売りの後になければならないからです。

次に、残りの操作を2つの状態に分割する必要があります。1つは購入後の残り(株式を保持)であり、もう1つは販売後の残り(現時点では株式を保持していない)です。

トランザクション数kにも制限があることに注意してください。つまり、k> 0の前提でのみ操作できます。

    上記のすべては、網羅的な列挙用です。在庫問題には3つの変数があります。1つ目は日数、2つ目は許可されるトランザクションの最大数です。

3番目は現在の保持状態です(通常、1は保持を意味し、0は保持しないことを意味します)。これらの状態のすべての組み合わせを組み立てるために、3次元の数値を使用します。

dp[i][k][0 or 1]
0 <= i <= n-1, 1 <= k <= K
n为天数,K为最多交易数
此问题共n * K * 2种状态,全部穷举就能够搞定
for 0 <= i <= n:
    for 1 <= k <= K:
        for s in {0,1}:
            dp[i][k][s] = max{buy , sell , rest}

最後に必要な答えはdp [n-1] [K] [0]です。つまり、最終日に最大k個のトランザクションが許可され、どれだけの利益を得ることができるかです。

3:状態移行フレームワーク

以上で、状態の一覧が完成しました。各状態のオプションと状態の更新方法を検討し、保持状態のみを確認します。

状態遷移図を描きます。

状態遷移図を通して、各状態(0と1)がどのように転送されるかを明確に見ることができます。

この図に従って、状態遷移方程式1を書きましょう。

dp [i] [k] [0] = max(dp [i-1] [k] [0]、dp [i-1] [k] [1] +価格[i])

それが最大です(残りを選択し、販売を選択します)

状態遷移方程式の説明:今日、私は2つの理由で株式を保有していません。

1)昨日は開催しなかった、今日は休憩を選んだので、今日も開催しなかった。

2)昨日は株を持っていたのですが、売れたので、今日は株を持っていませんでした。

状態遷移方程式2:

dp [i] [k] [1] = max(dp [i-1] [k] [1]、dp [i-1] [k-1] [0]-価格[i])

それが最大です(残りを選択し、購入を選択します)

状態遷移方程式の説明:

今日私は株を持っています、2つの可能性があります:

1)昨日株を持っていて、今日は休憩を選択しました。

2)昨日は株を持っていなかったのですが、今日は買うことにしました。

4.注意が必要な事項

利益の変化とkの変化

買う場合は利益から価格[i]を引く必要があります。売る場合は利益に価格[i]を加える必要があります。

今日の最大の利益は、2つの可能な選択肢のうちの大きい方です。そして、kの限界に注意を払い、

購入を選択すると、kが1減少するため、販売時にkを再度変更する必要はありません。

規範事例

dp [-1] [k] [0] = 0

説明:iは0から開始するため、i = -1はまだ開始していないことを意味し、この時点での利益は0です。

dp [-1] [k] [1] =負の無限大

説明:現時点ではまだ開始されていないため、在庫を保持することはできません。

dp [i] [0] [0] = 0;

説明:kは1から始まるので、k = 0は取引がまったく許可されないことを意味します。現時点では、利益はもちろん0です。

V.まとめ

base case
dp[-1][k][0] = dp[i][0][0] = 0
dp[-1][k][1] = dp[i][0][1] = -无穷
状态转移方程
dp[i][k][0] = max(dp[i-1][k][0],dp[i-1][k][1] +prices[i])
dp[i][k][1] = max(dp[i-1][k][1],dp[i-1][k-1][0] - prices[i])

プログラミングの補足:配列インデックスを-1にプログラムして、オーバーフローしないようにする方法。

2:実際の戦闘トピック

トピック1:株式を売買するのに最適な時期(k = 1)

(件名リンク:https//leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/

配列価格が与えられると、そのi番目の要素prices [i]は、i日目の特定の株式の価格を表します。

この株は特定の日にのみ購入し、将来の別の日に売却することを選択できます。あなたが得ることができる最大の利益を計算するためのアルゴリズムを設計します。

このトランザクションから得られる最大の利益を返します。利益を上げられない場合は、0を返します。

例1:

入力:[7,1,5,3,6,4]
出力:5
説明:2日目に購入(株価= 1)、5日目に販売(株価= 6)。最大利益= 6- 1 = 5。
     販売価格は購入価格よりも高くする必要があるため、利益を7-1 = 6にすることはできません。同時に、購入する前に株式を販売することはできません。

アイデア:テンプレートを直接適用し、dp [i] [1] = Math.max(dp [i-1] [1]、-prices [i])に注意してください。

その中には-prices [i]があります。k= 1であるため、購入の機会は1つだけです。

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        int[][] dp = new int[n][2];
        for(int i =0;i<n;i++){
            if(i-1 == -1){
                dp[i][0] = 0;
                dp[i][1] = -prices[i];
                continue;
            }
            dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
            dp[i][1] = Math.max(dp[i-1][1],-prices[i]);
        }
        return dp[n-1][0];
    }
}

最適化されたコード:

2次元配列は使用せず、変数のみを使用します

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        int dp_i_0 =0;
        int dp_i_1 = Integer.MIN_VALUE;
        for(int i =0;i<n;i++){
            dp_i_0 = Math.max(dp_i_0,dp_i_1+prices[i]);
            dp_i_1 = Math.max(dp_i_1,-prices[i]);
        }
        return dp_i_0;
    }
}

トピック2:株式を売買するのに最適な時期2(k =正の無限大)

(件名リンク:https//leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/

配列が与えられた場合、そのi番目の要素は、i番目の日の特定の株式の価格です。

あなたが得ることができる最大の利益を計算するためのアルゴリズムを設計します。できるだけ多くのトランザクションを完了することができます(株式を複数回売買する)。

注:同時に複数の取引に参加することはできません(再度購入する前に、以前の株式を売却する必要があります)。

例1:

入力:[7,1,5,3,6,4]
出力:7
説明:2日目に購入(株価= 1)、3日目に販売(株価= 5)、この取引所は利益= 5-1 = 4。
     次に、4日目に購入(株価= 3)、5日目に販売(株価= 6)します。この交換により、利益= 6-3 = 3を得ることができます。

欲張りアルゴリズム:翌日が当日よりも大きい限り、当日に買い、翌日に売ります

class Solution {
    public int maxProfit(int[] prices) {
        int res = 0;
        for(int i =0;i <prices.length-1;i++){
            if(prices[i] < prices[i+1]){
                res = res+prices[i+1]-prices[i];
            }
        }
        return res;
    }
}

動的計画法

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
         int dp_i_0 = 0;
         int dp_i_1 = Integer.MIN_VALUE;
         for(int i =0;i<n;i++){
             dp_i_0 = Math.max(dp_i_0,dp_i_1+prices[i]);
             dp_i_1 = Math.max(dp_i_1,dp_i_0-prices[i]);
         }
         return dp_i_0;
    }
}

トピック3:株式を売買するのに最適な時期3(k = 2)

(件名リンク:https//leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/

配列が与えられた場合、そのi番目の要素は、i番目の日の特定の株式の価格です。

あなたが得ることができる最大の利益を計算するためのアルゴリズムを設計します。最大2つのトランザクションを完了することができます。

注:同時に複数の取引に参加することはできません(再度購入する前に、以前の株式を売却する必要があります)。

例1:

入力:価格= [3,3,5,0,0,3,1,4]
出力:6
説明:

4日目(株価= 0)に買い、6日目(株価= 3)に売ります。この交換は利益= 3-0 = 3になります。
次に、7日目に購入(株価= 1)し、8日目に販売します(株価= 4)。この交換により、利益= 4-1 = 3を得ることができます。

アイデア:k = 2は最初の質問k = 1とは異なり、2番目の質問k =正の無限大です。k = 1は基本ケースに近いため、正の無限大はkを考慮しないことと同じです。

この質問では、k = 2です。これは、kが任意の正の整数であることに相当します。

最も原始的な動的伝達方程式があります

dp [i] [k] [0] = max(dp [i-1] [k] [0]、dp [i-1] [k] [1] + prices [i])

dp [i] [k] [1] = max(dp [i-1] [k] [1]、dp [i-1] [k-1] [0]-価格[i])

上記の状態遷移方程式は、要約した網羅的なフレームワークに従って記述されています。つまり、

すべての状態を使い果たします。しかし、最初の2つの質問では、kは網羅的な状態で簡略化されており、この質問では、kを考慮する必要があります。

したがって、kを網羅的にリストする必要があります。

アイデア:最も独創的なフレームワークを使用して、変数kのループの別のレイヤーを追加します

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        int max_k = 2;
        int[][][] dp = new int[n][max_k+1][2];
        for(int i =0;i<n;i++){
            for(int k=max_k;k>=1;k--){
                if(i==0){
                    dp[i][k][0] =0;
                    dp[i][k][1] = -prices[i];
                    continue;
                }
                dp[i][k][0] = Math.max(dp[i-1][k][0],dp[i-1][k][1]+prices[i]);
                dp[i][k][1] = Math.max(dp[i-1][k][1],dp[i-1][k-1][0]-prices[i]);
            }
        }
        return dp[n-1][max_k][0];
    }
}

注:1)配列の-1を処理することを忘れないでください

          2)continueステートメントを忘れないでください

トピック4:株式を売買するのに最適な時期3(k =任意の整数)

(件名リンク:https//leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/

整数配列の価格が与えられた場合、そのi番目の要素prices [i]は、i番目の日の特定の株式の価格です。

あなたが得ることができる最大の利益を計算するためのアルゴリズムを設計します。最大でk個のトランザクションを完了できます。

注:同時に複数の取引に参加することはできません(再度購入する前に、以前の株式を売却する必要があります)。

例1:

入力:k = 2、価格= [2,4,1]
出力:2
説明:1日目に購入(株価= 2)、2日目に販売(株価= 4)。この交換により、利益= 4-2 = 2。

アイデア:最も独創的なテンプレートを適用し、forループに注意を払います

class Solution {
    public int maxProfit(int k, int[] prices) {
        int n= prices.length;
        int[][][] dp = new int[n][k+1][2];
        if(n==0)return 0;
        for(int i =0;i<n;i++){
            for(int j =k;j>0;j--){
                if(i==0){
                    dp[i][j][0] =0;
                    dp[i][j][1] = -prices[i];
                    continue;
                }
                dp[i][j][0] =  Math.max(dp[i-1][j][0],dp[i-1][j][1]+prices[i]);
                dp[i][j][1] =  Math.max(dp[i-1][j][1],dp[i-1][j-1][0]-prices[i]);
            }
        }
        return dp[n-1][k][0];
    }
}

質問5:株を売買するのに最適な時期には凍結期間が含まれます

(件名リンク:https//leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/

整数配列が与えられると、i番目の要素はi番目の日の株価を表します。

最大利益を計算するアルゴリズムを設計します。次の制約の下で、できるだけ多くのトランザクションを完了することができます(株式を複数回売買する)。

同時に複数の取引に参加することはできません(再度購入する前に前の株式を売却する必要があります)。
株式を売却した後、翌日には購入できません(つまり、凍結期間は1日です)。
例:

入力:[1,2,3,0,2]
出力:3 
説明:対応するトランザクションステータスは次のとおりです:[購入、販売、凍結期間、購入、販売]

アイデア:追加の制約を追加します。これは、各売りの1日後に取引を続行するのと同じです。

プログラミング時には、その2次元配列の形式を使用することも、変数を直接使用して格納することもできます。

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        int dp_i_0 =0;
        int dp_i_1 = Integer.MIN_VALUE;
        int dp_pre =0;
        for(int i =0;i<n;i++){
            int temp = dp_i_0;
            dp_i_0 = Math.max(dp_i_0,dp_i_1+prices[i]);
            dp_i_1 = Math.max(dp_i_1, dp_pre-prices[i]);
            dp_pre = temp;
        }
        return dp_i_0;
    }
}

注:一時的なdp_preを使用して、前日の状態を保存します(現時点では保持されていません)。

トピック6:株式を売買するのに最適な時期には手数料が含まれます

(件名リンク:https//leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/

整数配列の価格が与えられます。ここで、i番目の要素はi番目の日の株価を表し、負でない整数の料金は株式の取引料金を表します。

取引は何度でも完了できますが、取引ごとに手数料を支払う必要があります。すでに株式を購入している場合は、売却する前に株式を購入することはできません。

得られた最大利益を返します。

注:ここでの取引とは、株式の売買の全過程を指し、取引ごとに手数料を支払うだけで済みます。

例1:

入力:価格= [1、3、2、8、4、9]、手数料= 2
出力:8
説明:達成できる最大利益:  
ここで購入価格[0] = 1
ここで販売価格[3] = 8
購入ここでの価格[4] = 4
ここでの販売価格[5] = 9
総利益:((8-1)-2)+((9-4)-2)= 8。

アイデア:コードフレームワークを使用し、購入時に1つの料金を差し引くだけです

class Solution {
    public int maxProfit(int[] prices, int fee) {
        int n = prices.length;
        int dp_i_0 = 0;
        int dp_i_1 = Integer.MIN_VALUE;
        for(int i =0;i<n;i++){
            dp_i_0 = Math.max(dp_i_0,dp_i_1+prices[i]);
            dp_i_1 = Math.max(dp_i_1,dp_i_0-prices[i]-fee);
        }
        return dp_i_0;
    }
}

参照

https://www.cnblogs.com/hanyuhuang/p/11083384.html

 

 

 

 

 

 

 

 

 

 

おすすめ

転載: blog.csdn.net/yezonghui/article/details/113791178
おすすめ