当前位置:   article > 正文

动态规划扫清股票问题_动态规划股票问题为什么当前状态只和前一天状态有关

动态规划股票问题为什么当前状态只和前一天状态有关

力扣上有若干道买股票求最大收益的问题,而这些问题都是可以由动态规划解决的,而且本质上的思想也是基本一致,这里将6道题目进行总结。

121. Best Time to Buy and Sell Stock

Question

Say you have an array for which the ith element is the price of a given stock on day i.

If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit.

Note that you cannot sell a stock before you buy one.

Example 1:

Input: [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
             Not 7-1 = 6, as selling price needs to be larger than buying price.
  • 1
  • 2
  • 3
  • 4

Example 2:

Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
  • 1
  • 2
  • 3

Solution

DP:
  • dp[i][0]:第 i 天结束时没有持有股票的最大收益;
  • dp[i][1]:第 i 天结束时持有股票的最大收益;
  • base case:
    • dp[0][0] = 0,第一天结束时没有股票则收益一定为零;
    • dp[0][1] = -prices[0],第一天结束时持有股票则买入股票需支付 prices[0] 的价格;
  • state transition:
    • dp[i][0] = max ( dp[i - 1][0] , dp[i - 1][1] + prices[i] ) :第 i 天结束没有股票;可能是因为昨天结束就没有股票,还有可能今天结束将股票卖出(而这种情况要求昨天结束时一定有股票)。
    • dp[i][1] = max ( dp[i - 1][1] , -prices[i] ) :第 i 天结束有股票,可能是因为昨天结束就有股票;还有可能今天刚买了股票,而由于股票交易只允许发生一次,所以今天买了以前一定没有买过,故状态与之前无关,即为 -prices[i]
  • returndp[n - 1][0],即最后一天没有股票时的最大收益。
class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) {
            return 0;
        }
        int n = prices.length;
        int[][] dp = new int[n][2];
        dp[0][0] = 0; // base case
        dp[0][1] = -prices[0]; // base case
        for (int i = 1; i < n; i++) {
            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];
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

由于在状态转移时,当前状态只和上一状态有关,故可以进行空间优化:

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) {
            return 0;
        }
        int n = prices.length;
        int max_nostock = 0; // base case
        int max_withstock = -prices[0]; // base case
        for (int i = 1; i < n; i++) {
            max_nostock = Math.max(max_nostock, max_withstock + prices[i]);
            max_withstock = Math.max(max_withstock, -prices[i]);
        }
        return max_nostock;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
Update min and calculate max

思路其实很简单,即:如果在第 i 天卖出股票,那么为了让收益最大,那么一定要在 i 之前股票最便宜的一天买入股票,故可实现代码如下:

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length < 2) {
            return 0;
        }
        int min = prices[0]; // min price before ith day
        int max = prices[1] - prices[0]; // max profit
        for (int i = 2; i < prices.length; i++) {
            if (prices[i - 1] < min) {
                min = prices[i - 1];
            }
            max = Math.max(prices[i] - min, max);
        }
        if (max < 0) {
            return 0;
        }
        return max;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

122. Best Time to Buy and Sell Stock II

Question

Say you have an array prices for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as many transactions as you like (i.e., buy one and sell one share of the stock multiple times).

Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).

Example 1:

Input: [7,1,5,3,6,4]
Output: 7
Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4.
             Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3.
  • 1
  • 2
  • 3
  • 4

Example 2:

Input: [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
             Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are
             engaging multiple transactions at the same time. You must sell before buying again.
  • 1
  • 2
  • 3
  • 4
  • 5

Example 3:

Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
  • 1
  • 2
  • 3

Constraints:

  • 1 <= prices.length <= 3 * 10 ^ 4
  • 0 <= prices[i] <= 10 ^ 4

Solution

DP:
  • dp[i][0]:第 i 天结束时没有持有股票的最大收益;
  • dp[i][1]:第 i 天结束时持有股票的最大收益;
  • base case:
    • dp[0][0] = 0,第一天结束时没有股票则收益一定为零;
    • dp[0][1] = -prices[0],第一天结束时持有股票则买入股票需支付 prices[0] 的价格;
  • state transition:
    • dp[i][0] = max ( dp[i - 1][0] , dp[i - 1][1] + prices[i] ) :第 i 天结束没有股票;可能是因为昨天结束就没有股票,还有可能今天结束将股票卖出(而这种情况要求昨天结束时一定有股票)。
    • dp[i][1] = max ( dp[i - 1][1] , dp[i - 1][0] - prices[i] ) :第 i 天结束有股票,可能是因为昨天结束就有股票;还有可能今天刚买了股票,和上题的区别是股票交易可以发生若干次。
  • returndp[n - 1][0],即最后一天没有股票时的最大收益。
class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) {
            return 0;
        }
        int n = prices.length;
        int[][] dp = new int[n][2];
        dp[0][0] = 0; // base case
        dp[0][1] = -prices[0]; // base case
        for (int i = 1; i < n; i++) {
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
        }
        return dp[n - 1][0];
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

状态转移时只和上一状态有关,故可以空间优化:

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) {
            return 0;
        }
        int n = prices.length;
        int max_nostock = 0; // base case
        int max_withstock = -prices[0]; // base case
        for (int i = 1; i < n; i++) {
            int tmp = max_nostock;
            max_nostock = Math.max(max_nostock, max_withstock + prices[i]);
            max_withstock = Math.max(max_withstock, tmp - prices[i]);
        }
        return max_nostock;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
Greedy

分析部分转载自liweiwei1419大佬的题解

贪心算法的直觉:由于不限制交易次数,只要今天股价比昨天高,就交易

下面对这个算法进行几点说明:

该算法仅可以用于计算,但计算的过程并不是真正交易的过程,但可以用贪心算法计算题目要求的最大利润。下面说明等价性:以 [1, 2, 3, 4] 为例,这 4 天的股价依次上升,按照贪心算法,得到的最大利润是:

res =  (prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])
    =  prices[3] - prices[0]
  • 1
  • 2

仔细观察上面的式子,按照贪心算法,在下标为 1、2、3 的这三天,做的操作应该是买进昨天的,卖出今天的,虽然这种操作题目并不允许,但是它等价于:在下标为 0 的那一天买入,在下标为 3 的那一天卖出。
这道题 「贪心」 的地方在于,对于 「今天的股价 - 昨天的股价」,得到的结果有 3 种可能:① 正数,② 0,③负数。贪心算法的决策是: 只加正数 。

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length < 2) {
            return 0;
        }
        int n = prices.length;
        int max = 0;
        for (int i = 1; i < n; i++) {
            if (prices[i] > prices[i - 1]) {
                max += prices[i] - prices[i - 1];
            }
        }
        return max;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

123. Best Time to Buy and Sell Stock III

Question

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most two transactions.

Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).

Example 1:

Input: prices = [3,3,5,0,0,3,1,4]
Output: 6
Explanation: Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1 = 3.
  • 1
  • 2
  • 3
  • 4

Example 2:

Input: prices = [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are engaging multiple transactions at the same time. You must sell before buying again.
  • 1
  • 2
  • 3
  • 4

Example 3:

Input: prices = [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
  • 1
  • 2
  • 3

Example 4:

Input: prices = [1]
Output: 0
  • 1
  • 2

Constraints:

  • 1 <= prices.length <= 10^5
  • 0 <= prices[i] <= 10^5

Solution

DP:

将买入股票定义为进行一次交易

  • dp[i][j][0]:第 i 天结束时没有持有股票且经过了 j 次交易的最大收益;
  • dp[i][j][1]:第 i 天结束时持有股票且经过了 j 次交易的最大收益;
  • base case:
    • dp[0][1][0] = Integer.MIN_VALUE / 2,第一天结束时没有股票且经过了一次交易,这是不可能的,故设置为一个小值,而为了防止整数溢出,故选择 Integer.MIN_VALUE / 2
    • dp[0][1][1] = -prices[0],第一天结束时持有股票且经过了1次交易则买入股票需支付 prices[0] 的价格;
    • dp[0][2][0] = Integer.MIN_VALUE / 2
    • d[0][2][1] = Ingeger.MIN_VALUE / 2
  • state transition:
    • dp[i][2][0] = max ( dp[i - 1][2][0] , dp[i - 1][2][1] + prices[i] ) :第 i 天结束经过了2次交易且没有股票,可能是因为昨天结束就经过了2次交易且没有股票;还有可能今天结束将股票卖出(而这种情况要求昨天结束时一定有股票)。
    • dp[i][2][1] = max ( dp[i - 1][2][1] , dp[i - 1][1][0] - prices[i] ) :第 i 天结束时经过了2次交易且有股票,可能是因为昨天结束就有股票;还有可能今天刚买了股票(即今天才发生第二次交易),则昨天的状态应为发生过1次交易且没有股票。
    • dp[i][1][0] = max ( dp[i - 1][1][0] , dp[i - 1][1][1] + prices[i] ) :第 i 天结束经过了1次交易且没有股票,可能是因为昨天结束就经过了1次交易且没有股票;还有可能今天结束将股票卖出(而这种情况要求昨天结束时一定有股票)。
    • dp[i][1][1] = max ( dp[i - 1][1][1] , -prices[i] ) :第 i 天结束时经过了1次交易且有股票,可能是因为昨天结束就有股票;还有可能今天刚买了股票(即今天才发生第一次交易)。
  • returnmax ( dp[n - 1][1][0] , dp[n - 1][2][0] ) ,即最后一天没有股票时的最大收益(可能经历了1次交易,可能经历了2次交易)。

最后的 return 为什么要取经历1次交易和2次交易的更大者呢?

考虑 [1, 2, 3, 4, 5] 这种情况,显然经历了2次交易的收益为

profit_2 = profit_trade1 + profit_trade2 = (2 - 1) + (4 - 3) = 2
  • 1

而经历了1次交易的收益为

profit_1 = profit_trade = 5 - 1 = 4
  • 1

所以不一定经过2次交易的会赚的多,所以需要二者比较取较大。

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) {
            return 0;
        }
        int n = prices.length;
        int[][][] dp = new int[n][3][2];
        dp[0][1][0] = Integer.MIN_VALUE / 2;
        dp[0][1][1] = -prices[0];
        dp[0][2][0] = Integer.MIN_VALUE / 2;
        dp[0][2][1] = Integer.MIN_VALUE / 2;
        for (int i = 1; i < n; i++) {
            dp[i][2][0] = Math.max(dp[i - 1][2][0], dp[i - 1][2][1] + prices[i]);
            dp[i][2][1] = Math.max(dp[i - 1][2][1], dp[i - 1][1][0] - prices[i]);
            dp[i][1][0] = Math.max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i]);
            dp[i][1][1] = Math.max(dp[i - 1][1][1], -prices[i]);
        }
        int max = Math.max(dp[n - 1][2][0], dp[n - 1][1][0]);
        if (max < 0) {
            return 0;
        }
        return max;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

进行空间优化后的代码如下:

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) {
            return 0;
        }
        int n = prices.length;
        int dp_i10 = Integer.MIN_VALUE / 2;
        int dp_i11 = -prices[0];
        int dp_i20 = Integer.MIN_VALUE / 2;
        int dp_i21 = Integer.MIN_VALUE / 2;
        for (int i = 1; i < n; i++) {
            int a = Math.max(dp_i20, dp_i21 + prices[i]);
            int b = Math.max(dp_i21, dp_i10 - prices[i]);
            int c = Math.max(dp_i10, dp_i11 + prices[i]);
            int d = Math.max(dp_i11, -prices[i]);
            dp_i20 = a;
            dp_i21 = b;
            dp_i10 = c;
            dp_i11 = d;
        }
        int max = Math.max(dp_i20, dp_i10);
        if (max < 0) {
            return 0;
        }
        return max;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

188. Best Time to Buy and Sell Stock IV

Question

You are given an integer array prices where prices[i] is the price of a given stock on the ith day.

Design an algorithm to find the maximum profit. You may complete at most k transactions.

Notice that you may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again).

Example 1:

Input: k = 2, prices = [2,4,1]
Output: 2
Explanation: Buy on day 1 (price = 2) and sell on day 2 (price = 4), profit = 4-2 = 2.
  • 1
  • 2
  • 3

Example 2:

Input: k = 2, prices = [3,2,6,5,0,3]
Output: 7
Explanation: Buy on day 2 (price = 2) and sell on day 3 (price = 6), profit = 6-2 = 4. Then buy on day 5 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
  • 1
  • 2
  • 3

Constraints:

  • 0 <= k <= 10^9
  • 0 <= prices.length <= 1000
  • 0 <= prices[i] <= 1000

Solution

将买入股票定义为进行一次交易

  • dp[i][j][0]:第 i 天结束时没有持有股票且经过了 j 次交易的最大收益;
  • dp[i][j][1]:第 i 天结束时持有股票且经过了 j 次交易的最大收益;
  • base case:
    • dp[0][1][0] = Integer.MIN_VALUE / 2,第一天结束时没有股票且经过了一次交易,这是不可能的,故设置为一个小值,而为了防止整数溢出,故选择 Integer.MIN_VALUE / 2
    • dp[0][1][1] = -prices[0],第一天结束时持有股票且经过了1次交易则买入股票需支付 prices[0] 的价格;
    • dp[0][j][0] = Integer.MIN_VALUE / 2,when j ≥ 2
    • d[0][j][1] = Ingeger.MIN_VALUE / 2,when j ≥ 2
  • state transition:
    • dp[i][j][0] = max ( dp[i - 1][j][0] , dp[i - 1][j][1] + prices[i] ) :第 i 天结束经过了 j 次交易且没有股票,可能是因为昨天结束就经过了 j 次交易且没有股票;还有可能今天结束将股票卖出(而这种情况要求昨天结束时一定有股票)。
    • dp[i][j][1] = max ( dp[i - 1][j][1] , dp[i - 1][j - 1][0] - prices[i] ) :第 i 天结束时经过了 j 次交易且有股票,可能是因为昨天结束就有股票;还有可能今天又买了股票(即昨天结束发生了 j - 1 次交易且没有股票)则今天发生了第 j 次交易。
  • returnmax ( dp[n - 1][j][0] ) ,即最后一天没有股票时的最大收益(可能经历了1次交易,也可能经历了多次交易)。

本题 k 的取值范围为 [0, 10^9],也就是说如果将 dp[][][] 数组真的按照第二维度为 k 定义的话,那必然会造成内存溢出的,所以应该考虑到,对于长度为 nprices 数组来说,最多可以进行多少次交易呢?

答案显而易见,是 ⌊n / 2⌋。也就是说当 k 大于该值时,本地就变成了123. Best Time to Buy and Sell Stock III题,即交易次数不限。

class Solution {
    public int maxProfit(int k, int[] prices) {
        if (prices == null || prices.length == 0 || k == 0) {
            return 0;
        }
        int n = prices.length;
        
        if (k > n / 2) {
            return maxProfitKNoLimit(prices);
        }
        
        int[][][] dp = new int[n][k + 1][2];
        
        // base case
        dp[0][1][1] = -prices[0];
        dp[0][1][0] = Integer.MIN_VALUE / 2;
        for (int i = 2; i <= k; i++) {
            dp[0][i][1] = Integer.MIN_VALUE / 2;
            dp[0][i][0] = Integer.MIN_VALUE / 2;
        }
        
        for (int i = 1; i < n; i++) {
            for (int j = 1; j <= k; j++) {
                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]);
            }
        }
        
        int max = 0;
        for (int i = 1; i <= k; i++) {
            max = Math.max(max, dp[n - 1][i][0]);
        }
        if (max < 0) {
            return 0;
        }
        return max;
    }

    private int maxProfitKNoLimit(int[] prices) {
        if (prices == null || prices.length == 0) {
            return 0;
        }
        int n = prices.length;
        int max_nostock = 0;
        int max_withstock = -prices[0];
        for (int i = 1; i < n; i++) {
            int tmp = max_nostock;
            max_nostock = Math.max(max_nostock, max_withstock + prices[i]);
            max_withstock = Math.max(max_withstock, tmp - prices[i]);
        }
        return max_nostock;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

将第一维的天数优化掉,可以得到下面的空间优化代码:

class Solution {
    public int maxProfit(int k, int[] prices) {
        if (prices == null || prices.length == 0 || k == 0) {
            return 0;
        }
        int n = prices.length;
        if (k > n / 2) {
            return maxProfitKNoLimit(prices);
        }
        int[][] dp = new int[k + 1][2];
        dp[1][1] = -prices[0];
        dp[1][0] = Integer.MIN_VALUE / 2;
        for (int i = 2; i <= k; i++) {
            dp[i][1] = Integer.MIN_VALUE / 2;
            dp[i][0] = Integer.MIN_VALUE / 2;
        }
        for (int i = 1; i < n; i++) {
            for (int j = 1; j <= k; j++) {
                int a = Math.max(dp[j][0], dp[j][1] + prices[i]);
                int b = Math.max(dp[j][1], dp[j - 1][0] - prices[i]);
                dp[j][0] = a;
                dp[j][1] = b;
            }
        }
        int max = 0;
        for (int i = 1; i <= k; i++) {
            max = Math.max(max, dp[i][0]);
        }
        if (max < 0) {
            return 0;
        }
        return max;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

309. Best Time to Buy and Sell Stock with Cooldown

Question

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions:

  • You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
  • After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)

Example:

Input: [1,2,3,0,2]
Output: 3 
Explanation: transactions = [buy, sell, cooldown, buy, sell]
  • 1
  • 2
  • 3

Solution

DP:

考虑加入冻结后当天结束时的状态组合有:①有股票且冻结;②没股票且冻结;③没股票且未冻结。(有股票时冻结与否第二天都不能够买入股票,故不需要再分两种情况讨论)。

  • dp[i][0]:第 i 天结束时持有股票的最大收益;
  • dp[i][1]:第 i 天结束时没有持有股票且冻结的最大收益;
  • dp[i][2]:第 i 天结束时没有持有股票且未冻结的最大收益。
  • base case:
    • dp[0][0] = -prices[0],第一天结束时持有股票则买入股票需支付 prices[0] 的价格;
    • dp[0][1] = dp[0][2] = 0,第一天结束时没有股票收益默认为 0
  • state transition:
    • dp[i][0] = max ( dp[i - 1][0] , dp[i - 1][2] - prices[i] ) :第 i 天结束有股票;可能是因为昨天结束就没有股票;还有可能今天买入了股票,而今天可以买入股票则昨天一定处于冻结状态。
    • dp[i][1] = dp[i][0] + prices[i]:第 i 天结束没有股票且冻结,之所以冻结一定是今天卖了股票,则昨天一定是有股票状态。
    • dp[i][2] = max ( dp[i - 1][1] , dp[i - 1][2]) :第 i 天结束没有股票且未冻结,则昨天一定没有股票,但是可以是昨天本来就为冻结,或者是昨天冻结而今天解冻。
  • returnmax ( dp[n - 1][1], dp[n - 1][2] ),即最后一天没股票时(可以是冻结状态)的最大收益。
class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) {
            return 0;
        }
        int n = prices.length;
        int[][] dp = new int[n][3];
        dp[0][0] = -prices[0];
        for (int i = 1; i < n; i++) {
            dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][2] - prices[i]);
            dp[i][1] = dp[i - 1][0] + prices[i];
            dp[i][2] = Math.max(dp[i - 1][1], dp[i - 1][2]);
        }
        return Math.max(dp[n - 1][1], dp[n - 1][2]);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

将第一维天数优化掉后有如下空间复杂度为 O(1) 的代码:

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) {
            return 0;
        }
        int n = prices.length;
        int max_withstock = -prices[0];
        int max_nostockCooldown = 0;
        int max_nostockNoCooldown = 0;
        for (int i = 1; i < n; i++) {
            int a = Math.max(max_withstock, max_nostockNoCooldown - prices[i]);
            int b = max_withstock + prices[i];
            int c = Math.max(max_nostockCooldown, max_nostockNoCooldown);
            max_withstock = a;
            max_nostockCooldown = b;
            max_nostockNoCooldown = c;
        }
        return Math.max(max_nostockCooldown, max_nostockNoCooldown);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

714. Best Time to Buy and Sell Stock with Transaction Fee

Question

Your are given an array of integers prices, for which the i-th element is the price of a given stock on day i; and a non-negative integer fee representing a transaction fee.

You may complete as many transactions as you like, but you need to pay the transaction fee for each transaction. You may not buy more than 1 share of a stock at a time (ie. you must sell the stock share before you buy again.)

Return the maximum profit you can make.

Example 1:

Input: prices = [1, 3, 2, 8, 4, 9], fee = 2
Output: 8
Explanation: The maximum profit can be achieved by:
Buying at prices[0] = 1Selling at prices[3] = 8Buying at prices[4] = 4Selling at prices[5] = 9The total profit is ((8 - 1) - 2) + ((9 - 4) - 2) = 8.
  • 1
  • 2
  • 3
  • 4

Note:

0 < prices.length <= 50000.

0 < prices[i] < 50000.

0 <= fee < 50000.

Solution

DP:

这道题和就没有任何区别,就是购买股票时多花了手续费的钱,或者理解为卖出股票时少赚了手续费的钱。

  • dp[i][0]:第 i 天结束时没有持有股票的最大收益;
  • dp[i][1]:第 i 天结束时持有股票的最大收益;
  • base case:
    • dp[0][0] = 0,第一天结束时没有股票则收益一定为零;
    • dp[0][1] = -prices[0] - fee,第一天结束时持有股票则买入股票需支付 prices[0] 的价格即手续费;
  • state transition:
    • dp[i][0] = max ( dp[i - 1][0] , dp[i - 1][1] + prices[i] ) :第 i 天结束没有股票;可能是因为昨天结束就没有股票,还有可能今天结束将股票卖出(而这种情况要求昨天结束时一定有股票)。
    • dp[i][1] = max ( dp[i - 1][1] , dp[i - 1][0] - prices[i] - fee) :第 i 天结束有股票,可能是因为昨天结束就有股票;还有可能今天刚买了股票,同时要多花手续费。
  • returndp[n - 1][0],即最后一天没有股票时的最大收益。

直接上优化了空间的代码吧。。。

class Solution {
    public int maxProfit(int[] prices, int fee) {
        if (prices == null || prices.length == 0) {
            return 0;
        }
        int n = prices.length;
        int max_nostock = 0;
        int max_withstock = -prices[0] - fee;
        for (int i = 1; i < n; i++) {
            int tmp = max_nostock;
            max_nostock = Math.max(max_nostock, max_withstock + prices[i]);
            max_withstock = Math.max(max_withstock, tmp - prices[i] - fee);
        }
        return max_nostock;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号