青蛙過(guò)河 和 買賣股票

青蛙過(guò)河1

給你一個(gè)下標(biāo)從 0 開(kāi)始的整數(shù)數(shù)組 stones ,數(shù)組中的元素 嚴(yán)格遞增 ,表示一條河中石頭的位置。

一只青蛙一開(kāi)始在第一塊石頭上,它想到達(dá)最后一塊石頭,然后回到第一塊石頭。同時(shí)每塊石頭 至多 到達(dá) 一次。

一次跳躍的 長(zhǎng)度 是青蛙跳躍前和跳躍后所在兩塊石頭之間的距離。

更正式的,如果青蛙從 stones[i] 跳到 stones[j] ,跳躍的長(zhǎng)度為 |stones[i] - stones[j]| 。
一條路徑的 代價(jià) 是這條路徑里的 最大跳躍長(zhǎng)度 。

請(qǐng)你返回這只青蛙的 最小代價(jià) 。



思路1

題目中描述的是:

 一只青蛙一開(kāi)始在第一塊石頭上,它想到達(dá)最后一塊石頭,然后回到第一塊石頭。同時(shí)每塊石頭至多到達(dá)一次。
我的想法是:先將問(wèn)題轉(zhuǎn)化。將題目中這只青蛙的回程(即從最后一塊石頭返回第一塊石頭的路徑)反向,看成是第二只青蛙從第一塊石頭跳到最后一塊石頭。這樣問(wèn)題就變成:

有兩只青蛙一開(kāi)始都在第一塊石頭,它們都需要到達(dá)最后一塊石頭,同時(shí)每塊石頭至多到到達(dá)一次,求這兩只青蛙的最大跳躍長(zhǎng)度。
接下來(lái)采用 貪心策略,讓這兩只青蛙從第一塊石頭跳到最后一塊石頭。假設(shè)有 n 塊石頭,考慮一般性,我們不妨假設(shè) n >= 3,接下來(lái)我們不妨先按 n 的奇偶性分別來(lái)模擬一下:

首先,我們不妨讓第一只青蛙先跳,第二只青蛙后跳,兩只青蛙交替著跳。

當(dāng) n 為奇數(shù)時(shí),不妨設(shè) n = 5,[0 1 2 3 4]:

第一只青蛙第 1 步:0 -> 1

第二只青蛙第 1 步:0 -> 2

第一只青蛙第 2 步:1 -> 3

第二只青蛙第 2 步:2 -> 4,第二只青蛙到達(dá)最后一塊石頭,停止。

第一只青蛙第 3 步:3 -> 4,第一只青蛙到達(dá)最后一塊石頭,停止。

最后,

第一只青蛙跳過(guò)的路徑為:0 -> 1 -> 3 -> 4

第二只青蛙跳過(guò)的路徑為:0 -> 2 -> 4

然后我們求兩只青蛙的最大跳躍長(zhǎng)度。觀察兩只青蛙跳過(guò)的路徑,不難發(fā)現(xiàn),第二只青蛙的第一跳 0 -> 2 跨越了第一只青蛙的第一跳 0 -> 1,第二只青蛙的最后一跳 2 -> 4 跨越了第一只青蛙的最后一跳 3 -> 4,所以第一只青蛙的第一跳 1 -> 2 和最后一跳 3 -> 4 不可能是最大跳躍。也就是說(shuō)我們只需要求 0 -> 2,1 -> 3, 2 -> 4 這三跳的的最大長(zhǎng)度即可,即:

ans = max(stones[2] - stones[0], stones[3] - stones[1], stones[4] - stones[2])
寫成代碼就是:

ans = 0
for i in range(2, n):
    ans = max(ans, stones[i] - stones[i - 2])

當(dāng) n 為偶數(shù)時(shí),不妨設(shè) n = 6,[0 1 2 3 4 5]:

第一只青蛙第 1 步:0 -> 1

第二只青蛙第 1 步:0 -> 2

第一只青蛙第 2 步:1 -> 3

第二只青蛙第 2 步:2 -> 4

第一只青蛙第 3 步:3 -> 5,第一只青蛙到達(dá)最后一塊石頭,停止。

第二只青蛙第 3 步:4 -> 5,第二只青蛙到達(dá)最后一塊石頭,停止。

最后,

第一只青蛙跳過(guò)的路徑為:0 -> 1 -> 3 -> 5

第二只青蛙跳過(guò)的路徑為:0 -> 2 -> 4 -> 5

然后我們求兩只青蛙的最大跳躍長(zhǎng)度。觀察兩只青蛙跳過(guò)的路徑,不難發(fā)現(xiàn),第二只青蛙的第一跳 0 -> 2 跨越了第一只青蛙的第一跳 0 -> 1,第一只青蛙的最后一跳 3 -> 5 跨越了第二只青蛙的最后一跳 4 -> 5,所以第一只青蛙的第一跳 1 -> 2 和第二只青蛙的最后一跳 4 -> 5 不可能是最大跳躍。也就是說(shuō)我們只需要求 0 -> 2,1 -> 3, 2 -> 4, 3 -> 5 這四跳的的最大長(zhǎng)度即可,即:

ans = max(stones[2] - stones[0], stones[3] - stones[1], stones[4] - stones[2], stones[5] - stones[3])
寫成代碼就是:

ans = 0
for i in range(2, n):
    ans = max(ans, stones[i] - stones[i - 2])

綜上所述,當(dāng) n >= 3 時(shí),求最大跳躍長(zhǎng)度的代碼有:

ans = 0
for i in range(2, n):
    ans = max(ans, stones[i] - stones[i - 2])

最后,當(dāng) n == 2 時(shí),兩只青蛙只需一跳就能從第一塊石頭跳到最后一塊石頭,最大跳躍長(zhǎng)度就是 stones[1] - stones[0]~

于是,我們有求最大跳躍長(zhǎng)度的完整代碼如下:

class Solution:
    def maxJump(self, stones: List[int]) -> int:
        n = len(stones)

        if n == 2:
            return stones[1] - stones[0]

        ans = 0
        for i in range(2, n):
            ans = max(ans, stones[i] - stones[i - 2])

        return ans
        
最后,我們將 ans 初始化為 stones[1] - stones[0],就不需要將 n == 2 的情況單獨(dú)拿出來(lái)了~

class Solution:
    def maxJump(self, stones: List[int]) -> int:
        n = len(stones)

        ans = stones[1] - stones[0]
        for i in range(2, n):
            ans = max(ans, stones[i] - stones[i - 2])

        return ans

思路2

image.png

答案

class Solution {
    public int maxJump(int[] stones) {
        int result = 0;
        if(stones.length==2){
            result = stones[1]-stones[0];
        }else {
            for (int i = 2; i < stones.length; i++) {
                result = Math.max(result,(stones[i]-stones[i-2]));
            }
        }
        return result;
    }
}

青蛙過(guò)河2

一只青蛙想要過(guò)河。 假定河流被等分為若干個(gè)單元格,并且在每一個(gè)單元格內(nèi)都有可能放有一塊石子(也有可能沒(méi)有)。 青蛙可以跳上石子,但是不可以跳入水中。

給你石子的位置列表 stones(用單元格序號(hào) 升序 表示), 請(qǐng)判定青蛙能否成功過(guò)河(即能否在最后一步跳至最后一塊石子上)。開(kāi)始時(shí), 青蛙默認(rèn)已站在第一塊石子上,并可以假定它第一步只能跳躍 1 個(gè)單位(即只能從單元格 1 跳至單元格 2 )。

如果青蛙上一步跳躍了 k 個(gè)單位,那么它接下來(lái)的跳躍距離只能選擇為 k - 1、k 或 k + 1 個(gè)單位。 另請(qǐng)注意,青蛙只能向前方(終點(diǎn)的方向)跳躍。


比較簡(jiǎn)單的動(dòng)態(tài)規(guī)劃。dp[i][k]表示能否從第i個(gè)石頭前面的任意一個(gè)石頭j用k步跳到第i個(gè)石頭,狀態(tài)轉(zhuǎn)移方程為dp[i][k]=dp[j][k-1]||dp[j][k]||dp[j][k+1],只需要判斷當(dāng)i==len-1時(shí)能否成功即可。

答案

    public boolean canCross(int[] stones) {
        Boolean result = false;
        for (int i = 1; i < stones.length; i++) {
            if (stones[i] - stones[i - 1] > i) {
                return result;
            }
        }
        boolean[][] dp = new boolean[stones.length][stones.length];
        dp[0][0] = true;
        for (int i = 1; i < stones.length; i++) {
            for (int j = i - 1; j >= 0; j--) {
                int k = stones[i] - stones[j];
                if (j + 1 < k) {//最大的步都滿足不了
                    break;
                }
                dp[i][k] = dp[j][k - 1] || dp[j][k] || dp[j][k + 1];
                if (i == stones.length - 1 && dp[i][k]) {
                    return true;
                }
            }
        }
        return result;
    }

買賣股票1

給定一個(gè)數(shù)組 prices ,它的第 i 個(gè)元素 prices[i] 表示一支給定股票第 i 天的價(jià)格。

你只能選擇 某一天 買入這只股票,并選擇在 未來(lái)的某一個(gè)不同的日子 賣出該股票。設(shè)計(jì)一個(gè)算法來(lái)計(jì)算你所能獲取的最大利潤(rùn)。

返回你可以從這筆交易中獲取的最大利潤(rùn)。如果你不能獲取任何利潤(rùn),返回 0 。

示例 1:

輸入:[7,1,5,3,6,4]
輸出:5
解釋:在第 2 天(股票價(jià)格 = 1)的時(shí)候買入,在第 5 天(股票價(jià)格 = 6)的時(shí)候賣出,最大利潤(rùn) = 6-1 = 5 。
注意利潤(rùn)不能是 7-1 = 6, 因?yàn)橘u出價(jià)格需要大于買入價(jià)格;同時(shí),你不能在買入前賣出股票。
示例 2:

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



思路還是挺清晰的,還是DP思想:

記錄【今天之前買入的最小值】
計(jì)算【今天之前最小值買入,今天賣出的獲利】,也即【今天賣出的最大獲利】
比較【每天的最大獲利】,取最大值即可

public class Solution {
    public int maxProfit(int[] prices) {
        int result=0;
        int minP = prices[0];
        for (int i = 1; i < prices.length; i++) {
            result = Math.max(result,prices[i]-minP);
            minP = Math.min(minP,prices[i]);
        }
        return result;
    }
}

模板方法

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

dp[i-1][1]是我之前賣股票賺的錢 因?yàn)橹挥幸淮钨I賣,所以就沒(méi)這個(gè)

買賣股票2

給你一個(gè)整數(shù)數(shù)組 prices ,其中 prices[i] 表示某支股票第 i 天的價(jià)格。

在每一天,你可以決定是否購(gòu)買和/或出售股票。你在任何時(shí)候 最多 只能持有 一股 股票。你也可以先購(gòu)買,然后在 同一天 出售。

返回 你能獲得的 最大 利潤(rùn) 。

示例 1:

輸入:prices = [7,1,5,3,6,4]
輸出:7
解釋:在第 2 天(股票價(jià)格 = 1)的時(shí)候買入,在第 3 天(股票價(jià)格 = 5)的時(shí)候賣出, 這筆交易所能獲得利潤(rùn) = 5 - 1 = 4 。
隨后,在第 4 天(股票價(jià)格 = 3)的時(shí)候買入,在第 5 天(股票價(jià)格 = 6)的時(shí)候賣出, 這筆交易所能獲得利潤(rùn) = 6 - 3 = 3 。
總利潤(rùn)為 4 + 3 = 7 。
示例 2:

輸入:prices = [1,2,3,4,5]
輸出:4
解釋:在第 1 天(股票價(jià)格 = 1)的時(shí)候買入,在第 5 天 (股票價(jià)格 = 5)的時(shí)候賣出, 這筆交易所能獲得利潤(rùn) = 5 - 1 = 4 。
總利潤(rùn)為 4 。
示例 3:

輸入:prices = [7,6,4,3,1]
輸出:0
解釋:在這種情況下, 交易無(wú)法獲得正利潤(rùn),所以不參與交易可以獲得最大利潤(rùn),最大利潤(rùn)為 0 。



算法題(×) 腦筋急轉(zhuǎn)彎題( √ )

掃描一遍 只要后一天比前一天大 就把這兩天的差值加一下

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

買賣股票3

給定一個(gè)數(shù)組,它的第 i 個(gè)元素是一支給定的股票在第 i 天的價(jià)格。

設(shè)計(jì)一個(gè)算法來(lái)計(jì)算你所能獲取的最大利潤(rùn)。你最多可以完成 兩筆 交易。

注意:你不能同時(shí)參與多筆交易(你必須在再次購(gòu)買前出售掉之前的股票)。

示例 1:

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

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

輸入:prices = [7,6,4,3,1]
輸出:0
解釋:在這個(gè)情況下, 沒(méi)有交易完成, 所以最大利潤(rùn)為 0。
示例 4:

輸入:prices = [1]
輸出:0


對(duì)于任意一天考慮四個(gè)變量:
fstBuy: 在該天第一次買入股票可獲得的最大收益
fstSell: 在該天第一次賣出股票可獲得的最大收益
secBuy: 在該天第二次買入股票可獲得的最大收益
secSell: 在該天第二次賣出股票可獲得的最大收益
分別對(duì)四個(gè)變量進(jìn)行相應(yīng)的更新, 最后secSell就是最大
收益值(secSell >= fstSell)

    public int maxProfit(int[] prices) {
        if(prices.length==1){
            return 0;
        }
        int mai1 = -prices[0];//第一天買進(jìn)就是虧的
        int man1 = 0;
        int mai2 = -prices[0] - prices[1];
        int man2 = 0;
        for (int i = 1; i < prices.length; i++) {
            mai1 = Math.max(mai1, -prices[i]);//今天買入就虧今天這么多錢
            man1 = Math.max(man1, prices[i] + mai1);//買進(jìn)的收益加上賣出的收益
            mai2 = Math.max(mai2, man1 - prices[i]);
            man2 = Math.max(man2, prices[i] + mai2);
        }
        return man2;
    }

模板方法

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

            }
        }
        return dp[n-1][2*k-1];
    }
}

關(guān)鍵在于至多買賣兩次,這意味著可以買賣一次,可以買賣兩次,也可以不買賣。
接來(lái)下我用動(dòng)態(tài)規(guī)劃五部曲詳細(xì)分析一下:

  1. 確定dp數(shù)組以及下標(biāo)的含義
    一天一共就有五個(gè)狀態(tài),
  2. 沒(méi)有操作
  3. 第一次買入
  4. 第一次賣出
  5. 第二次買入
  6. 第二次賣出
    dp[i][j]中 i表示第i天,j為 [0 - 4] 五個(gè)狀態(tài),dp[i][j]表示第i天狀態(tài)j所剩最大現(xiàn)金。
  7. 確定遞推公式
    需要注意:dp[i][1],表示的是第i天,買入股票的狀態(tài),并不是說(shuō)一定要第i天買入股票,這是很多同學(xué)容易陷入的誤區(qū)。
    達(dá)到dp[i][1]狀態(tài),有兩個(gè)具體操作:
  • 操作一:第i天買入股票了,那么dp[i][1] = dp[i-1][0] - prices[i]
  • 操作二:第i天沒(méi)有操作,而是沿用前一天買入的狀態(tài),即:dp[i][1] = dp[i - 1][1]
    那么dp[i][1]究竟選 dp[i-1][0] - prices[i],還是dp[i - 1][1]呢?
    一定是選最大的,所以 dp[i][1] = max(dp[i-1][0] - prices[i], dp[i - 1][1]);
    同理dp[i][2]也有兩個(gè)操作:
  • 操作一:第i天賣出股票了,那么dp[i][2] = dp[i - 1][1] + prices[i]
  • 操作二:第i天沒(méi)有操作,沿用前一天賣出股票的狀態(tài),即:dp[i][2] = dp[i - 1][2]
    所以dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2])
    同理可推出剩下?tīng)顟B(tài)部分:
    dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]);
    dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
  1. dp數(shù)組如何初始化
    第0天沒(méi)有操作,這個(gè)最容易想到,就是0,即:dp[0][0] = 0;
    第0天做第一次買入的操作,dp[0][1] = -prices[0];
    第0天做第一次賣出的操作,這個(gè)初始值應(yīng)該是多少呢?
    首先賣出的操作一定是收獲利潤(rùn),整個(gè)股票買賣最差情況也就是沒(méi)有盈利即全程無(wú)操作現(xiàn)金為0,
    從遞推公式中可以看出每次是取最大值,那么既然是收獲利潤(rùn)如果比0還小了就沒(méi)有必要收獲這個(gè)利潤(rùn)了。
    所以dp[0][2] = 0;
    第0天第二次買入操作,初始值應(yīng)該是多少呢?應(yīng)該不少同學(xué)疑惑,第一次還沒(méi)買入呢,怎么初始化第二次買入呢?
    第二次買入依賴于第一次賣出的狀態(tài),其實(shí)相當(dāng)于第0天第一次買入了,第一次賣出了,然后在買入一次(第二次買入),那么現(xiàn)在手頭上沒(méi)有現(xiàn)金,只要買入,現(xiàn)金就做相應(yīng)的減少。
    所以第二次買入操作,初始化為:dp[0][3] = -prices[0];
    同理第二次賣出初始化dp[0][4] = 0;
  2. 確定遍歷順序
    從遞歸公式其實(shí)已經(jīng)可以看出,一定是從前向后遍歷,因?yàn)閐p[i],依靠dp[i - 1]的數(shù)值。
  3. 舉例推導(dǎo)dp數(shù)組
    以輸入[1,2,3,4,5]為例
    大家可以看到紅色框?yàn)樽詈髢纱钨u出的狀態(tài)。
    現(xiàn)在最大的時(shí)候一定是賣出的狀態(tài),而兩次賣出的狀態(tài)現(xiàn)金最大一定是最后一次賣出。
    所以最終最大利潤(rùn)是dp[4][4]
    以上五部都分析完了,不難寫出如下代碼:

買賣股票4

給定一個(gè)整數(shù)數(shù)組 prices ,它的第 i 個(gè)元素 prices[i] 是一支給定的股票在第 i 天的價(jià)格。

設(shè)計(jì)一個(gè)算法來(lái)計(jì)算你所能獲取的最大利潤(rùn)。你最多可以完成 k 筆交易。

注意:你不能同時(shí)參與多筆交易(你必須在再次購(gòu)買前出售掉之前的股票)。

示例 1:

輸入:k = 2, prices = [2,4,1]
輸出:2
解釋:在第 1 天 (股票價(jià)格 = 2) 的時(shí)候買入,在第 2 天 (股票價(jià)格 = 4) 的時(shí)候賣出,這筆交易所能獲得利潤(rùn) = 4-2 = 2 。
示例 2:

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

class Solution {
    public int maxProfit(int k, int[] prices) {
        //buy[i]表示買入i次股票后的利潤(rùn)
        int[] buy = new int[k+1];
        //sell[i]表示完成i筆交易后的利潤(rùn)
        int[] sell = new int[k+1];
        //初始所有都第一次買入,后面需要確定假設(shè)所有第一次買入和當(dāng)下第一次買入比較
        Arrays.fill(buy,-prices[0]);
        for(int i = 1;i < prices.length;i++){
            //最多支持k次交易
            for(int j = 1;j <= k;j++){
                //買入j次后的最大利潤(rùn)為:Math.max(當(dāng)前利潤(rùn),上一筆交易的利潤(rùn)-當(dāng)天的股票價(jià)格)
                buy[j] = Math.max(buy[j],sell[j-1]-prices[i]);
                //完成j次交易后的最大利潤(rùn)為:Math.max(當(dāng)前利潤(rùn),當(dāng)前利潤(rùn)+本次交易后所得的利潤(rùn))
                sell[j] = Math.max(sell[j],buy[j]+prices[i]);
            }
        }
        return sell[k];
    }
}

模板方法

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

            }
        }
        return dp[n-1][2*k-1];
    }
}

買賣股票含冷凍期

給定一個(gè)整數(shù)數(shù)組prices,其中第 prices[i] 表示第 i 天的股票價(jià)格 。

設(shè)計(jì)一個(gè)算法計(jì)算出最大利潤(rùn)。在滿足以下約束條件下,你可以盡可能地完成更多的交易(多次買賣一支股票):

賣出股票后,你無(wú)法在第二天買入股票 (即冷凍期為 1 天)。
注意:你不能同時(shí)參與多筆交易(你必須在再次購(gòu)買前出售掉之前的股票)。

示例 1:

輸入: prices = [1,2,3,0,2]
輸出: 3
解釋: 對(duì)應(yīng)的交易狀態(tài)為: [買入, 賣出, 冷凍期, 買入, 賣出]
示例 2:

輸入: prices = [1]
輸出: 0


    public int maxProfit(int[] prices) {
        if (prices.length < 1) {
            return 0;
        }
        //由于可以無(wú)限次交易,所以只定義兩個(gè)維度,第一個(gè)維度是天數(shù),第二個(gè)維度表示是否持有股票,0表示不持有,1表示持有,2表示過(guò)渡期
        int[][] dp = new int[prices.length][3];
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        dp[0][2] = 0;
        for (int i = 1; i < prices.length; i++) {
            //第i天不持有股票的情況有兩種
            //a.第i-1天也不持有股票
            //b.第i-1天是過(guò)渡期
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2]);
            //第i天持有股票有兩種情況
            //a.第i-1天也持有股票,第i天不操作,
            //b.第i-1天不持有股票,在第i天買入
            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
            //第i天是冷凍期只有一種情況,第i-1天持有股票且賣出
            dp[i][2] = dp[i - 1][1] + prices[i];
        }
        return Math.max(dp[prices.length - 1][0], dp[prices.length - 1][2]);
    }

方法2

這個(gè)是一套模板

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

買賣股票含手續(xù)費(fèi)

給定一個(gè)整數(shù)數(shù)組 prices,其中 prices[i]表示第 i 天的股票價(jià)格 ;整數(shù) fee 代表了交易股票的手續(xù)費(fèi)用。

你可以無(wú)限次地完成交易,但是你每筆交易都需要付手續(xù)費(fèi)。如果你已經(jīng)購(gòu)買了一個(gè)股票,在賣出它之前你就不能再繼續(xù)購(gòu)買股票了。

返回獲得利潤(rùn)的最大值。

注意:這里的一筆交易指買入持有并賣出股票的整個(gè)過(guò)程,每筆交易你只需要為支付一次手續(xù)費(fèi)。

示例 1:

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

輸入:prices = [1,3,7,5,10,3], fee = 3
輸出:6


這是一道動(dòng)態(tài)規(guī)劃的題目,我們可以定義兩個(gè)狀態(tài),buy代表當(dāng)前持有一只股票所能獲得的最大利潤(rùn),sell代表當(dāng)前沒(méi)有持有股票所能獲得的最大利潤(rùn)。

對(duì)于每一天,我們可以選擇買入或賣出股票,或者不進(jìn)行任何操作。如果選擇買入股票,那么當(dāng)前的利潤(rùn)就是前一天賣出股票獲得的利潤(rùn)減去當(dāng)天的股票價(jià)格和手續(xù)費(fèi);如果選擇賣出股票,那么當(dāng)前的利潤(rùn)就是前一天買入股票獲得的利潤(rùn)加上當(dāng)天的股票價(jià)格;如果不進(jìn)行任何操作,那么當(dāng)前的利潤(rùn)就是前一天的利潤(rùn)。

具體來(lái)說(shuō),我們可以使用以下的狀態(tài)轉(zhuǎn)移方程:

buy[i] = max(buy[i-1], sell[i-1] - prices[i] - fee)
sell[i] = max(sell[i-1], buy[i-1] + prices[i])

其中,i表示當(dāng)前的天數(shù),buy[i]代表第i天持有股票所獲得的最大利潤(rùn),sell[i]代表第i天沒(méi)有持有股票所獲得的最大利潤(rùn),prices[i]代表第i天的股票價(jià)格,fee代表手續(xù)費(fèi)。

最終,我們的答案就是sell[n-1],其中n是prices數(shù)組的長(zhǎng)度。

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

模板方法2

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

建議看這套模板

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv/solution/yi-tao-mo-ban-ji-xing-dai-ma-bi-zhao-yan-2t7x/

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

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

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