【LeetCode】動的プログラミング研修 (6)

123. 株を売買するのに最適な時期 III

クリックして表示:株式を売買するのに最適な時期 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が得られます。
例 2:
入力: 価格 = [1,2,3,4,5]
出力: 4
説明: 1 日目に購入 (株価 = 1)、5 日目に購入 (株価 = 5) 売り、このトランザクションは利益 = 5-1 = 4。
1日目と2日目に続けて株式を購入し、後で売却することはできないことに注意してください。
これは同時に複数の取引に関係するため、再度購入する前に以前の株式を売却する必要があります。

トピック分析

前回の株式発行と比較すると、手数料と凍結期間以外はほとんど同じですが、
取引回数が1回から2回(最大2回、または1回または0回)に変更可能です。

株式の購入から株式の売却まで、取引が完了したとみなされます。


ゼロトランザクション

価格は安い順に並んでいますので、いつ株を買っても売れば損失が発生しますので、
この期間中何もしなければ、この時点での利益は0となり、この時点で
取引が完了することは0件となります。時間

取引

価格が昇順の場合、初日に株を売り、価格が 5 になるまで何もせず、価格が 5 になっ
たら株を売り、このときの利益は 5-1=4 で
取引を完了します。今度は取引する


2つの取引

初日に株を買ったら、2日目に買っても利益が出ないので、2日目は何もせず、3日目に株を売ります。このときの利益
はは: 5-3=2


4日目に株を買って、価格が4元になるまで何もせず、
4元になったら株を売り、
このときの利益は、 4-0=4 となります
。 2 つのトランザクションが完了した場合の利益は次のとおりです: 4+2=6

現時点で 2 つのトランザクションを完了します

状態遷移方程式

dp[i]: i 日目終了後に得られる最大利益を示します。


位置 i には、買い状態と売り状態の 2 つの状態があります。f は
買い状態を表し、g は売り状態を表します。i
は i 日の終わりを表し
、j は取引数を表します

f[i][j]: i 日目の終了後、j 回の取引が完了し、この時点での最大利益が g[i][j]: であることを意味します。 - 日目、取引が
完了しました。 j 取引、売り状態で、現時点での最大利益

株式の購入と株式の売却の操作が完了すると、取引回数が変更されます


f[i][j] 状態遷移方程式

i-1 日目が買い状態であれば、i 日目は何もせず、i 日目も買い状態になります。
この場合、f[i][j]=f[i- 1][j];


i-1 日目が売り状態の場合、i 日目は買い状態になります。
株式の購入に対応する価格 [i] を差し引く必要があります。
この場合: f[i][j] =g[i-1][ j]-価格[i];


状態遷移方程式は次のとおりです。
f[i][j]=max(f[i-1][j],g[i-1][j]-price[i]);

g[i][j] 状態遷移方程式

i-1 日目が売り状態の場合、i 日目は何もしないと、i 日目も売り状態になります。 この場合:
g [i][j]=g[i- 1][j] ;


i-1日目が買い状態であれば、i日目は売り状態となり、買いから売りまでの状態が完了しているため、
売り株に相当する利益価格[i]を加算する必要があります。
i日目の取引回数+1がjになります このとき、jは元の回数に+1され、
i-1日目の取引回数は元の回数のままですので、株の買いから株の売りまでをj-1 とします
。j を 0 として取引の完了を計算し
、i-1 日目に株を買い、取引回数が 0 で、
i 日目に株を売ります。トランザクション数は1です

この場合: g[i][j]=f[i-1][j-1]+price[i];


状態遷移方程式は次のとおりです。
g[i][j]= max(g[i-1][j],f[i-1][j-1]+price[i]);

初期化

g[i][j] の状態遷移方程式では、j が 0 のとき、i-1 日に -1 個のトランザクションが完了すると、この状況は存在しないため、g[i][j] の状態は次のようになります
。伝達方程式が修正される


最初のステップでは、 g[i][j] を g[i-1][j] に代入するため、if ループ内で g[i-1][j] を g[i][j] に直接置き換えます
。 -1 トランザクションは回避できます


縦軸は i 日目の終了後、
横軸は i 番目のトランザクションの完了を示し
、縦軸が 0 の場合は、0 日目の終了後に 0/1/2 個のトランザクションが完了したことを意味します。
同日売買は利益が発生せず、取引回数も限られているため、その後
の結果に支障をきたさないように、0日目終了後、1/2取引が完了した時点で取引を終了させて​​いただきます。完了すると、すべて負の無限大に設定されます


f[0][0]: 0日目終了後は買い状態であることを示します。つまり、株を買うとお金がかかり、利益がマイナスになります。 f[0][0]=
-価格[0];


g[0][0]:0日目終了後、販売状態であることを示す(0日目終了後は手元に在庫がなく、販売できない状態に相当)何もしないと利益は 0) g[
0 ][0]=0;


負の無限大に対して INT_MIN を選択すると、
範囲外の問題が発生します。 負の無限大の選択-0x3f3f3f3f(0x3f3f3f3f は int の最大値の半分)

完全なコード

class Solution {
    
    
public:
    int maxProfit(vector<int>& prices) {
    
    
       int n=prices.size();
       //因为可能完成 0 1 2 三笔交易其中一种 所以定义为3列
       //负无穷大 若要设置为INT_MIN会发生越界问题
       //所以使用 -0x3f3f3f
       vector<vector<int>>f(n,vector<int>(3,-0x3f3f3f3f));
       vector<vector<int>>g(n,vector<int>(3,-0x3f3f3f3f));
       int i=0;
       int j=0;
       //初始化
       f[0][0]=-prices[0];
       g[0][0]=0;
       for(i=1;i<n;i++)
       {
    
    
           for(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=INT_MIN;
        //寻找最后一行g的最大值
        for(j=0;j<3;j++)
        {
    
    
            if(ret<g[n-1][j])
            {
    
    
                 ret=g[n-1][j];
            }
        }
        //返回最后一行g的最大值
        return ret;
    }
};

戻り値
最大利益は0トランザクション/1トランザクション/2トランザクションの可能性がある
ため fを使用した場合は、最後のポジションに達し、まだ買い状態であることを意味するため、最大利益にはなり得ませんので、gを使用し
、 g の最後の数を数えます 行の最大値 (0、1、2 の位置)

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

クリックして表示:株式を売買するのに最適な時期 IV


整数配列priceを指定すると、そのi番目の要素price[i]は、i日目の特定の株式の価格と整数kになります。
得られる最大利益を計算するアルゴリズムを設計します。最大 k 件のトランザクションを完了できます。言い換えれば、最大 k 回買うことができ、最大 k 回売ることができます。
注: 同時に複数の取引に参加することはできません (再度購入する前に、前の株式を売却する必要があります)。

例 1:
入力: k = 2、価格 = [2,4,1]
出力: 2
説明: 1 日目に購入 (株価 = 2)、2 日目に購入 (株価 = 4) 売り、このトランザクションは利益 = 4-2 = 2。
例 2:
入力: k = 2、価格 = [3,2,6,5,0,3]
出力: 7
説明: 2 日目に購入 (株価 = 2)、3 日目に購入 (株価がいつ売られるか)価格 = 6)、この取引は利益 = 6-2 = 4 を得ることができます。
次に、5 日目に買って (株価 = 0)、6 日目に売って (株価 = 3)、この取引では利益 = 3-0 = 3 を得ることができます。

トピック分析

この質問は基本的に株の売買に最適な時期 III と似ていますが、以前の最大 2 回 (0 1 2 3 の可能性) が k 回 ([0,k-1] の可能性) に変更されている点が異なります。


k は 2 で、最大の利益を求めるには最大 2 トランザクション (0 トランザクション、1 トランザクション、2 トランザクションの 3 つの可能性があります) を意味します。


初日に株を買って 2 日目に売ると、このときの利益は 4-2=2
3 日目は最終日なので何もしない、
つまり最大利益は 2 となります。

状態遷移方程式

(ベストタイミングⅣの質問は、分析段階のベストタイミングⅢと基本的に同じです)


dp[i]: i 日目終了後に得られる最大利益を示します。

位置 i には、買い状態と売り状態の 2 つの状態があります。f は
買い状態を表し、g は売り状態を表します。i
は i 日の終わりを表し
、j は取引数を表します

f[i][j]: i 日目の終了後、j 回の取引が完了し、この時点での最大利益が g[i][j]: であることを意味します。 - 日目、取引が
完了しました。 j 取引、売り状態で、現時点での最大利益

株式の購入と株式の売却の操作が完了すると、取引回数が変更されます

f[i][j] 状態遷移方程式

i-1 日目が買い状態であれば、i 日目は何もせず、i 日目も買い状態になります。
この場合、f[i][j]=f[i- 1][j];


i-1 日目が売り状態の場合、i 日目は買い状態になります。
株式の購入に対応する価格 [i] を差し引く必要があります。
この場合: f[i][j] =g[i-1][ j]-価格[i];


状態遷移方程式は次のとおりです。
f[i][j]=max(f[i-1][j],g[i-1][j]-price[i]);

g[i][j] 状態遷移方程式

i-1 日目が売り状態の場合、i 日目は何もしないと、i 日目も売り状態になります。 この場合:
g [i][j]=g[i- 1][j] ;


i-1日目が買い状態であれば、i日目は売り状態となり、買いから売りまでの状態が完了しているため、
売り株に相当する利益価格[i]を加算する必要があります。
i日目の取引回数+1がjになります このとき、jは元の回数に+1され、
i-1日目の取引回数は元の回数のままですので、株の買いから株の売りまでをj-1 とします
。j を 0 として取引の完了を計算し
、i-1 日目に株を買い、取引回数が 0 で、
i 日目に株を売ります。トランザクション数は1です

この場合: g[i][j]=f[i-1][j-1]+price[i];


状態遷移方程式は次のとおりです。
g[i][j]= max(g[i-1][j],f[i-1][j-1]+price[i]);

初期化

g[i][j] の状態遷移方程式では、j が 0 のとき、i-1 日に -1 個のトランザクションが完了すると、この状況は存在しないため、g[i][j] の状態は次のようになります
。伝達方程式が修正される


最初のステップでは、 g[i][j] を g[i-1][j] に代入するため、if ループ内で g[i-1][j] を g[i][j] に直接置き換えます。-1 トランザクションは
回避できます


k が 2 であると仮定すると、トランザクション 0、トランザクション 1、トランザクション 2 の 3 つの状況があります。

縦軸は i 日目の終了後、
横軸は i 番目のトランザクションの完了を示し
、縦軸が 0 の場合は、0 日目の終了後に 0/1/2 個のトランザクションが完了したことを意味します。
同日売買は利益が発生せず、取引回数も限られているため、その後
の結果に支障をきたさないように、0日目終了後、1/2取引が完了した時点で取引を終了させて​​いただきます。完了すると、すべて負の無限大に設定されます


f[0][0]: 0日目終了後は買い状態であることを示します。つまり、株を買うとお金がかかり、利益がマイナスになります。 f[0][0]=
-価格[0];


g[0][0]:0日目終了後、販売状態であることを示す(0日目終了後は手元に在庫がなく、販売できない状態に相当)何もしないと利益は 0) g[
0 ][0]=0;


負の無限大に対して int_min を選択すると、
範囲外の問題が発生します。 負の無限大の選択-0x3f3f3f3f(0x3f3f3f3f は int の最大値の半分)\

完全なコード

class Solution {
    
    
public:
    int maxProfit(int k, vector<int>& prices) {
    
    
       int n=prices.size();
       //将k进行优化
       k=min(k,n/2);
         //f 表示买入状态 g表示卖出状态
       //有[0,k-1] 笔交易
       vector<vector<int>>f(n,vector<int>(k+1,-0x3f3f3f));  
       vector<vector<int>>g(n,vector<int>(k+1,-0x3f3f3f));

       //初始化
       f[0][0]=-prices[0];
       g[0][0]=0;
       int i=0;
       int j=0;
       for(i=1;i<n;i++)
       {
    
    
           for(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]);
              }
           }
       }
       //寻找g的最后一行的最大值
       int ret=INT_MIN;
       for(j=0;j<=k;j++)
       {
    
    
           if(ret<g[n-1][j])
           {
    
    
               ret=g[n-1][j];
           }
       }
       //返回g的最后一行的最大值
       return ret;
    }
};

20日ある場合、k(トランザクション数)は30で、
最大トランザクション数は10しかないので、k=min(k,n/2); (n/2は最大トランザクション数を表します)を最適化します。

53. 最大サブアレイ合計

クリックして表示:最大サブ配列合計


状態遷移方程式

i で終わるすべてのサブ配列 (i の位置自体、i と i-1 の位置の組み合わせ、i と i-2 の位置の組み合わせ、i と添字 0 の位置の組み合わせなど) を取得します。生成されたサブ配列を取得します。 - その中の配列
と最大のもの

dp[i]: i 位置要素で終わるすべての部分配列の最大合計を示します。


dp[i] は 2 つのカテゴリに分類できます。


1.この場合、i 位置の要素自体 (長さは 1) : dp[i]= nums[i]


2. i 位置要素は前の要素と結合されます (長さは 1 より大きい)

必要なのは位置 i で終わるサブ配列の最大合計であるため、最初に位置 i-1 で終わるサブ配列の最大合計を見つける必要があります。つまり、nums[i] を dp[i- 1]、これは
i 位置が最後の部分配列の最大合計です。
この場合: dp[i]=dp[i-1]+nums[i];

初期化

i が 0 の場合、それは範囲外の問題です

このような範囲外の問題を防ぐために、
展開された配列に仮想ノードが追加されます。仮想ノードの添字は 0 で、元の配列の要素の添字は 1 から始まります。

dp[1]の場合、dp[1]=max(nums[1],dp[0]+nums[1]) 結果に影響を与えないように、dp
[0]の値を0に設定します。

完全なコード

class Solution {
    
    
public:
    int maxSubArray(vector<int>& nums) {
    
    
       int n=nums.size();
       //dp作为扩展数组 所以比原数组大1
       vector<int>dp(n+1,0);

       int i=0;
       //因为dp表可能都为负,所以初始值为最小值
       int ret=INT_MIN;
       for(i=1;i<n+1;i++)
       {
    
    
           //当前下标i作为扩展数组dp的下标 
           //想要使用dp下标 找到对应 原数组nums对应值
           //应该使用i-1
           dp[i]=max(nums[i-1],dp[i-1]+nums[i-1]);
           //寻找dp表的最大值
           if(ret<dp[i])
           {
    
    
               ret=dp[i];
           }
       }

       return ret;

    }
};

おすすめ

転載: blog.csdn.net/qq_62939852/article/details/131462419