1. 程式人生 > 其它 >線性DP (買賣股票系列)

線性DP (買賣股票系列)

技術標籤:線性dp演算法演算法資料結構動態規劃

線性dp 大盜阿福

我們先看一個線性dp的經典題型
題目連結
題目大意就是,一個劫匪要劫店,這裡的報警系統是相鄰兩家都被盜的時候才會報警,讓你判斷在不觸發報警的情況下取得的最大價值.
這道題dp做狀態轉移方程為:
f[i][0]這i家店不搶的狀態,它可以由前一個不搶的狀態來獲取
也可以從前一個點被搶來獲取;
f[i][1]這家店搶劫的狀態,它只能從前一個不搶的狀態加上價值來獲取
所以就有下面的方程

			f[i][0]=max(f[i-1][0],f[i-1][1]);
			f[i][1]=f[i-1][0]+w[i];

程式碼實現

#include<iostream>
#include<math.h> #include<algorithm> using namespace std; const int maxn=1e5+5; int f[maxn][2]; int w[maxn]; int main() { int n,m,t; cin>>t; while(t--) { cin>>n; for(int i=1;i<=n;i++) { cin>>w[i]; } f[0][0]=0; f[0][1]=0; for(int i=1;i<=n;i++) { f[
i][0]=max(f[i-1][0],f[i-1][1]); f[i][1]=f[i-1][0]+w[i]; } cout<<max(f[n][1],f[n][0])<<endl; } }

121.買賣股票的最佳時機

題目連結
給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。
如果你最多只允許完成一筆交易(即買入和賣出一支股票一次),設計一個演算法來計算你所能獲取的最大利潤。
注意:你不能在買入股票前賣出股票。
示例一:

輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 =
6)的時候賣出,最大利潤 = 6-1 = 5 。 注意利潤不能是 7-1 = 6, 因為賣出價格需要大於買入價格;同時,你不能在買入前賣出股票。

程式碼實現

通過一個最大值和最小值去更新遍歷,找到符合的最大值和最小值相減.

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size()<=1)
            return 0;
        int minn=prices[0],maxx=0;
        for(int i=1;i<prices.size();i++)
        {
            maxx=max(maxx,prices[i]-minn);
            minn=min(minn,prices[i]);
        }
        return maxx;
    }
};

122.買賣股票的最佳時機 二

題目連結
給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。
設計一個演算法來計算你所能獲取的最大利潤。你可以儘可能地完成更多的交易(多次買賣一支股票)。
注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。
示例一:

輸入: [7,1,5,3,6,4]
輸出: 7
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 3 天(股票價格 = 5)的時候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。
     隨後,在第 4 天(股票價格 = 3)的時候買入,在第 5 天(股票價格 = 6)的時候賣出, 這筆交易所能獲得利潤 = 6-3 = 3

程式碼實現

用dp陣列去遍歷,f[][0]是手中沒票,f[][1]是手中有票

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        int f[n][2];
        f[0][0]=0;		   //第一天沒票為0
        f[0][1]=-prices[0];//第一天有票的情況,要買票
        for(int i=1;i<=n-1;i++)
        {
            f[i][0]=max(f[i-1][0],f[i-1][1]+prices[i]);//從有票到沒票,要加上價值
            f[i][1]=max(f[i-1][1],f[i-1][0]-prices[i]);//從沒票到有票,要減去價值
        }
        return f[n-1][0];//最後一天,沒票的情況最大,因為要賣票
    }
};

買賣股票的最佳時機 三

題目連結
給定一個數組,它的第 i 個元素是一支給定的股票在第 i 天的價格。
設計一個演算法來計算你所能獲取的最大利潤。你最多可以完成 兩筆 交易
注意: 你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

程式碼實現

題目連結
這道題我們也用dp來實現用三維陣列來標記狀態
f[天數][第幾次交易][手中有無票的狀態];
我們用買入買票的時候來當做一次交易
第i天第j次交易無票狀態可以通過前一天的j次交易無票狀態轉移
也可以通過前一天的j次交易有票狀態賣票轉移
第i天第j次交易有票狀態可以通過前一天的j次交易有票狀態轉移
也可以通過前一天的j-1次交易無票狀態賣票轉移

所以狀態轉移方程就是

 f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]+prices[i]);
 //第i天第j次交易無票狀態可以通過前一天的j次交易無票狀態轉移
 //也可以通過前一天的j次交易有票狀態賣票轉移
 f[i][j][1]=max(f[i-1][j][1],f[i-1][j-1][0]-prices[i]);
 //第i天第j次交易有票狀態可以通過前一天的j次交易有票狀態轉移
 //也可以通過前一天的j-1次交易無票狀態賣票轉移
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n=prices.size();
        int f[n][3][2];
        memset(f,0,sizeof(f));
        for(int i=0;i<=2;i++)
        {
            f[0][i][1]=-prices[0];
        }
        for(int i=1;i<=n-1;i++)
        {
            for(int j=1;j<=2;j++)
            {
                f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]+prices[i]);
                f[i][j][1]=max(f[i-1][j][1],f[i-1][j-1][0]-prices[i]);
            }
        }
        return f[n-1][2][0];
    }
};

買賣股票的最佳時機 四

題目連結
給定一個整數陣列 prices ,它的第 i 個元素 prices[i] 是一支給定的股票在第 i 天的價格。
設計一個演算法來計算你所能獲取的最大利潤。你最多可以完成 k 筆交易
注意:你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

程式碼實現

上一道題的升級,就是把兩次交易變為k次交易
f[天數][第幾次交易][手中有無票的狀態];
我們用買入買票的時候來當做一次交易
第i天第j次交易無票狀態可以通過前一天的j次交易無票狀態轉移
也可以通過前一天的j次交易有票狀態賣票轉移
第i天第j次交易有票狀態可以通過前一天的j次交易有票狀態轉移
也可以通過前一天的j-1次交易無票狀態賣票轉移

所以狀態轉移方程就是

 f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]+prices[i]);
 //第i天第j次交易無票狀態可以通過前一天的j次交易無票狀態轉移
 //也可以通過前一天的j次交易有票狀態賣票轉移
 f[i][j][1]=max(f[i-1][j][1],f[i-1][j-1][0]-prices[i]);
 //第i天第j次交易有票狀態可以通過前一天的j次交易有票狀態轉移
 //也可以通過前一天的j-1次交易無票狀態賣票轉移
class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
            if(prices.size()<=1)
                return 0;
            int n=prices.size();
            int f[n][k+1][2];
            memset(f,0,sizeof(f));
            for(int i=0;i<=k;i++)
            {
                f[0][i][1]=-prices[0];
            }
            for(int i=1;i<=n-1;i++)
            {
                for(int j=1;j<=k;j++)
                {
                    f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]+prices[i]);
                    f[i][j][1]=max(f[i-1][j][1],f[i-1][j-1][0]-prices[i]);
                }
            }
            return f[n-1][k][0];
    }
};

其實上面的兩題可以用空間優化,從三維變為二維,原因是每次狀態其實都是從上一個緊挨著的天數來進行的更新的,所以每次更新都是挨著的,不用再填一個天數的狀態來多加一維空間

最佳買賣股票時機含冷凍期

題目連結
給定一個整數陣列,其中第 i 個元素代表了第 i 天的股票價格 。​
設計一個演算法計算出最大利潤。在滿足以下約束條件下,你可以儘可能地完成更多的交易(多次買賣一支股票):
你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。
賣出股票後,你無法在第二天買入股票 (即冷凍期為 1 天)
示例一:

輸入: [1,2,3,0,2]
輸出: 3 
解釋: 對應的交易狀態為: [買入, 賣出, 冷凍期, 買入, 賣出]

程式碼實現

有三種狀態
f[i][0]為第i天賣掉票無票的第一天的狀態
f[i][1]為第i天有入票的狀態
f[i][2]為第i天為買票後的第二天後無票狀態
狀態轉移方程為

			f[i][1]=max(f[i-1][1],f[i-1][2]-prices[i]);
            f[i][0]=f[i-1][1]+prices[i];
            f[i][2]=max(f[i-1][2],f[i-1][0]);

最後還要比較一下f[n-1][2],f[n-1][0]的大小.因為f[n-1][0]是最後一天賣票的狀態

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

買賣股票的最佳時機含手續費

給定一個整數陣列 prices,其中第 i 個元素代表了第 i 天的股票價格 ;非負整數 fee 代表了交易股票的手續費用。
你可以無限次地完成交易,但是你每筆交易都需要付手續費。如果你已經購買了一個股票,在賣出它之前你就不能再繼續購買股票了。
返回獲得利潤的最大值。
注意:這裡的一筆交易指買入持有並賣出股票的整個過程,每筆交易你只需要為支付一次手續費。
示例一:

輸入: prices = [1, 3, 2, 8, 4, 9], fee = 2
輸出: 8
解釋: 能夠達到的最大利潤:  
在此處買入 prices[0] = 1
在此處賣出 prices[3] = 8
在此處買入 prices[4] = 4
在此處賣出 prices[5] = 9
總利潤: ((8 - 1) - 2) + ((9 - 4) - 2) = 8.

程式碼實現

這道題多加了一個手續費的問題,其實本質上和上面的題沒有什麼差別
就是在賣票的時候多減去一個數就行了。

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