zoukankan      html  css  js  c++  java
  • [LeetCode] 188. Best Time to Buy and Sell Stock IV

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

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

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

    Example 1:

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

    Example 2:

    Input: [3,2,6,5,0,3], k = 2
    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.

    买卖股票的最佳时机IV。这是股票系列的第四题了,题目基本设定还是跟前三个版本一样,但是这道题问的是如果你最多可以交易K次,请问你的最大收益是多少。

    经过前面的题,这道题依然需要用动态规划做。我参考了一个讲的很好的题解。这里我们第一次接触到三维的DP。这里dp[i][j][K]的定义是:到下标为 i 的天数为止(从 0 开始),到下标为 j 的交易次数(从 0 开始),以状态为K的情况下,最大收益是多少。这里的状态K用0或1分别表示不持有和持有股票,也只有这两种状态。

    这个题有几个边界条件需要排除。当K == 0的时候,也就是没有交易次数的时候,最大收益就是0,因为没有买也没有卖。当K >= 数组长度的一半的时候,这道题就转化为了版本二的贪心算法。因为K的次数包含了买和卖,换言之一次买卖就得占用两次交易次数,所以当K大于数组长度的一半的时候,用贪心算法算出最大收益即可。

    接下来是处理几种一般情况,

    当i == 0的时候,也就是第0天的时候,如果当前是持有状态,那么初始值就是-prices[0];如果当前是不持有状态,初始值是0

    当 i 不为0但是持有股票[i][j][1]的话,分两种情况

    如果交易次数j == 0,也就是不允许交易的话,当前位置持股的DP值 dp[i][j][1] 是前一天持股的DP值 dp[i - 1][j][1] 和今天买了股票 -prices[i] 之间的较大值

    如果交易次数 j 不为0,那么当前位置持股的DP值 dp[i][j][1] 是前一天持股的DP值dp[i - 1][j][1] 和前一天不持股但是买了股票 dp[i - 1][j - 1][0] - prices[i] 之间的较大值

    对于其他情形,也就是在某一天不持有股票的话dp[i][j][0],DP值就是 前一天不持有股票dp[i - 1][j][0] 和 前一天持有股票但是今天卖掉了 dp[i][j - 1][1] + prices[i] 之间的较大值

    因为最后返回的时候,利益更大的一定是在不持有的这个状态上,所以返回的是dp[len - 1][k - 1][0]

    时间O(n^2)

    空间O(n^3) - 三维矩阵

    Java实现

     1 class Solution {
     2     public int maxProfit(int k, int[] prices) {
     3         int len = prices.length;
     4         // 特判
     5         if (k == 0 || len < 2) {
     6             return 0;
     7         }
     8         if (k >= len / 2) {
     9             return greedy(prices, len);
    10         }
    11 
    12         // dp[i][j][K]:到下标为 i 的天数为止(从 0 开始),到下标为 j 的交易次数(从 0 开始)
    13         // 状态为 K 的最大利润,K = 0 表示不持股,K = 1 表示持股
    14         int[][][] dp = new int[len][k][2];
    15         // 初始化:把持股的部分都设置为一个较大的负值
    16         for (int i = 0; i < len; i++) {
    17             for (int j = 0; j < k; j++) {
    18                 dp[i][j][1] = -9999;
    19             }
    20         }
    21 
    22         // 编写正确代码的方法:对两个"基本状态转移方程"当 i - 1 和 j - 1 分别越界的时候,做特殊判断,赋值为 0 即可
    23         for (int i = 0; i < len; i++) {
    24             for (int j = 0; j < k; j++) {
    25                 if (i == 0) {
    26                     dp[i][j][1] = -prices[0];
    27                     dp[i][j][0] = 0;
    28                 } else {
    29                     if (j == 0) {
    30                         dp[i][j][1] = Math.max(dp[i - 1][j][1], -prices[i]);
    31                     } else {
    32                         // 基本状态转移方程 1
    33                         dp[i][j][1] = Math.max(dp[i - 1][j][1], dp[i - 1][j - 1][0] - prices[i]);
    34                     }
    35                     // 基本状态转移方程 2
    36                     dp[i][j][0] = Math.max(dp[i - 1][j][0], dp[i - 1][j][1] + prices[i]);
    37                 }
    38             }
    39         }
    40         // 说明:i、j 状态都是前缀性质的,只需返回最后一个状态
    41         return dp[len - 1][k - 1][0];
    42     }
    43 
    44     private int greedy(int[] prices, int len) {
    45         // 转换为股票系列的第 2 题,使用贪心算法完成,思路是只要有利润,就交易
    46         int res = 0;
    47         for (int i = 1; i < len; i++) {
    48             if (prices[i - 1] < prices[i]) {
    49                 res += prices[i] - prices[i - 1];
    50             }
    51         }
    52         return res;
    53     }
    54 }

    LeetCode 题目总结

  • 相关阅读:
    梦断代码阅读笔记03
    梦断代码阅读笔记01
    构建之法阅读笔记03
    《构建之法——现代软件工程》读书笔记(一)
    构建之法阅读笔记02
    TypeError: only integer scalar arrays can be converted to a scalar index
    LeetCode Easy: 33. Search in Rotated Sorted Array
    LeetCode Medium: 31. Next Permutation
    LeetCode Medium: 29. Divide Two Integers
    python中merge、concat用法
  • 原文地址:https://www.cnblogs.com/cnoodle/p/13698632.html
Copyright © 2011-2022 走看看