算法 LC 買賣股票的最佳時機 II

題目描述

給定一個數(shù)組 prices ,其中 prices[i] 表示股票第 i 天的價格。
在每一天,你可能會決定購買和/或出售股票。你在任何時候 最多 只能持有 一股 股票。你也可以購買它,然后在 同一天 出售。
返回 你能獲得的 最大 利潤 。

示例 1:

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

示例 2:

輸入: prices = [1,2,3,4,5]
輸出: 4
解釋: 在第 1 天(股票價格 = 1)的時候買入,在第 5 天 (股票價格 = 5)的時候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接連購買股票,之后再將它們賣出。因為這樣屬于同時參與了多筆交易,你必須在再次購買前出售掉之前的股票。
示例 3:

輸入: prices = [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。

題解

解題思路1 - 計算所有正增長

由于股票的購買沒有限制,那么把能賺錢的交易都累加上就是最大利潤了
每天的盈利都獲得,每天的虧損都規(guī)避。
所以最大收益就是每天比前一天的正差價總和

+ (int)maxProfit1:(NSArray *)prices {
    int ans = 0;
    for (int i=1; i<prices.count; i++) {
        if ([prices[i] intValue]>[prices[i-1] intValue]) {
            ans += [prices[i] intValue] - [prices[i-1] intValue];
        }
    }
    return ans;
}
    static public func maxProfit1(_ prices: [Int]) -> Int {
        var ans = 0;
        for i in 1..<prices.count {
            if prices[i] > prices[i-1] {
                ans += prices[i] - prices[i-1]
            }
        }
        return ans;
    }
解題思路 2 - 貪心算法

由于股票的購買沒有限制,因此整個問題等價于尋找x個不相交的區(qū)間(li,ri],使得x個區(qū)間的a[ri]-a[li]的和值最大。
x
∑ a[ri] - a[li]
i=1
其中l(wèi)i表示第li天買入的值,ri表示第ri天賣出

其中區(qū)間(li,ri]貢獻的價值為a[ri]-a[li],其實等價于(li,li+1],(li+1,li+2],...(ri-1,ri]這若干個區(qū)間長度為1的區(qū)間的價值和,即a[ri]-a[li] = (a[ri]-a[ri-1]) + (a[ri-1]-a[ri-2]) + ... + (a[li+1]-a[li]),因此問題可以簡化為找x個長度為1的區(qū)間(li,li+1],使得x個區(qū)間的a[li+1]-a[li]的和值最大

x
∑ a[li+1] - a[li] 值最大
I=1

貪心的角度考慮我們每次選擇貢獻大于0的區(qū)間即能使得答案最大化,因此最后答案為
n-1
∑ max{0,a[i] - a[i-1]}
i=1

+ (int)maxProfit2:(NSArray *)prices {
    int ans = 0;
    for (int i=1; i<prices.count; i++) {
        ans += MAX(0, [prices[i] intValue] - [prices[i-1] intValue]);
    }
    return ans;
}
    static public func maxProfit2(_ prices: [Int]) -> Int {
        var ans = 0;
        for i in 1..<prices.count {
            ans += max(0, prices[i] - prices[i-1])
        }
        return ans;
    }
解題思路 3 - 動態(tài)規(guī)劃

考慮到「不能同時參與多筆交易」,因此每天交易結(jié)束后只可能存在手里有一支股票或者沒有股票的狀態(tài)

定義狀態(tài)dp[i][0]表示第i天交易后手里沒有股票的最大利潤,dp[i][1]表示第i天交易完后手里持有一只股票的最大利潤,i從0開始

dp[i][0]的轉(zhuǎn)移方程,這一天交易完后手里沒有股票,那么可能的轉(zhuǎn)移狀態(tài)為前一天已經(jīng)沒有股票,即dp[i-1][0],或者前一天結(jié)束時持有股票,即dp[i-1][1],這時候我們要將其賣出并獲到當(dāng)天的收益prices[i],即dp[i-1][1]+prices[i],因此為了收益最大化,我們列出如下的轉(zhuǎn)移方程:
dp[i][0] = max{dp[i-1][0],dp[i-1][1]+prices[I]}

dp[i][1]的轉(zhuǎn)移方程,這一天交易完后手里還有股票,那么可能的轉(zhuǎn)移狀態(tài)為前一天手里就有股票,即dp[i-1][1],或者前一天結(jié)束時沒有股票,即dp[i-1][0],這時候我們需要買進股票并減少收益prices[i],即dp[i-1][0]-prices[i],因此為了收益最大化,我們列出如下的轉(zhuǎn)移方程:
dp[i][1] = max{dp[i-1][1],dp[i-1][0]-prices[I]}

對于初始狀態(tài),根據(jù)狀態(tài)定義我們可以知道第0天交易結(jié)束的時候dp[0][0] = 0,dp[0][1] = -prices[0]

因此,我們只要從前往后依次計算狀態(tài)即可。由于全部交易結(jié)束后,持有股票的收益一定低于不持有股票的收益,因而dp[n-1][0] 一定大于dp[n-1][1]的,因而最后的答案是dp[n-1][0]

+ (int)maxProfit3:(NSArray *)prices {
    int n = (int)prices.count;
    int dp[n][2];
    dp[0][0] = 0;
    dp[0][1] = -[prices[0] intValue];
    for (int i=1; i<n; i++) {
        dp[i][0] = MAX(dp[i-1][0], dp[i-1][1]+[prices[i] intValue]);
        dp[i][1] = MAX(dp[i-1][1], dp[i-1][0]-[prices[i] intValue]);
    }
    return dp[n-1][0];
}
    static public func maxProfit3(_ prices: [Int]) -> Int {
        let n = prices.count
        var dp = [[Int]](repeating: [Int](repeating: 0, count: 2), count: n)
        
        dp[0][0] = 0
        dp[0][1] = -prices[0]
        
        for i in 1..<n {
            dp[i][0] = max(dp[i-1][1]+prices[i], dp[i-1][0])
            dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])
        }
        return dp[n-1][0]
    }

優(yōu)化上述動態(tài)規(guī)劃

注意到上面的狀態(tài)轉(zhuǎn)移方程中,每一天的狀態(tài)只與前一天的狀態(tài)有關(guān),而與更早的狀態(tài)都無關(guān),因此我們不必存儲這些無關(guān)的狀態(tài),只需要將dp[i?1][0] 和dp[i?1][1] 存放在兩個變量中,通過它們計算出dp[i][0] 和dp[i][1] 并存回對應(yīng)的變量,以便于第i+1 天的狀態(tài)轉(zhuǎn)移即可。

+ (int)maxProfit4:(NSArray *)prices {
    int n = (int)prices.count;
    int dp0 = 0;
    int dp1 = -[prices[0] intValue];
    for (int i=1; i<n; i++) {
        int newDp0 = MAX(dp0, dp1+[prices[i] intValue]);
        int newDp1 = MAX(dp1, dp0-[prices[i] intValue]);
        dp0 = newDp0;
        dp1 = newDp1;
    }
    return dp0;
}
    static public func maxProfit4(_ prices: [Int]) -> Int {
        let n = prices.count
        var dp0:Int = 0
        var dp1:Int = -prices[0]
        
        for i in 1..<n {
            let newDp0:Int = max(dp0, dp1+prices[i])
            let newDp1:Int = max(dp0-prices[i],dp1)
            dp0 = newDp0
            dp1 = newDp1
        }
        return dp0
    }

參考 https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/x2zsx1/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容