Best Time to Buy and Sell Stock IV

Question

  1. Say you have an array for
  2. which the ith element is the price of a given stock on day i.
  3. Design an algorithm to find the maximum profit.
  4. You may complete at most k transactions.
  5. Example
  6. Given prices = [4,4,6,1,1,4,2,5], and k = 2, return 6.
  7. Note
  8. You may not engage in multiple transactions at the same time
  9. (i.e., you must sell the stock before you buy again).
  10. Challenge
  11. O(nk) time.

题解1

卖股票系列中最难的一道,较易实现的方法为使用动态规划,动规的实现又分为大约3大类方法,这里先介绍一种最为朴素的方法,过不了大量数据,会 TLE.

最多允许 k 次交易,由于一次增加收益的交易至少需要两天,故当 k >= n/2时,此题退化为卖股票的第二道题,即允许任意多次交易。当 k < n/2 时,使用动规来求解,动规的几个要素如下:

f[i][j] 代表第 i 天为止交易 k 次获得的最大收益,那么将问题分解为前 x 天交易 k-1 次,第 x+1 天至第 i 天交易一次两个子问题,于是动态方程如下:

  1. f[i][j] = max(f[x][j - 1] + profit(x + 1, i))

简便起见,初始化二维矩阵为0,下标尽可能从1开始,便于理解。

Python

  1. class Solution:
  2. """
  3. @param k: an integer
  4. @param prices: a list of integer
  5. @return: an integer which is maximum profit
  6. """
  7. def maxProfit(self, k, prices):
  8. if prices is None or len(prices) <= 1 or k <= 0:
  9. return 0
  10. n = len(prices)
  11. # k >= prices.length / 2 ==> multiple transactions Stock II
  12. if k >= n / 2:
  13. profit_max = 0
  14. for i in xrange(1, n):
  15. diff = prices[i] - prices[i - 1]
  16. if diff > 0:
  17. profit_max += diff
  18. return profit_max
  19. f = [[0 for i in xrange(k + 1)] for j in xrange(n + 1)]
  20. for j in xrange(1, k + 1):
  21. for i in xrange(1, n + 1):
  22. for x in xrange(0, i + 1):
  23. f[i][j] = max(f[i][j], f[x][j - 1] + self.profit(prices, x + 1, i))
  24. return f[n][k]
  25. # calculate the profit of prices(l, u)
  26. def profit(self, prices, l, u):
  27. if l >= u:
  28. return 0
  29. valley = 2**31 - 1
  30. profit_max = 0
  31. for price in prices[l - 1:u]:
  32. profit_max = max(profit_max, price - valley)
  33. valley = min(valley, price)
  34. return profit_max

C++

  1. class Solution {
  2. public:
  3. /**
  4. * @param k: An integer
  5. * @param prices: Given an integer array
  6. * @return: Maximum profit
  7. */
  8. int maxProfit(int k, vector<int> &prices) {
  9. if (prices.size() <= 1 || k <= 0) return 0;
  10. int n = prices.size();
  11. // k >= prices.length / 2 ==> multiple transactions Stock II
  12. if (k >= n / 2) {
  13. int profit_max = 0;
  14. for (int i = 1; i < n; ++i) {
  15. int diff = prices[i] - prices[i - 1];
  16. if (diff > 0) {
  17. profit_max += diff;
  18. }
  19. }
  20. return profit_max;
  21. }
  22. vector<vector<int> > f = vector<vector<int> >(n + 1, vector<int>(k + 1, 0));
  23. for (int j = 1; j <= k; ++j) {
  24. for (int i = 1; i <= n; ++i) {
  25. for (int x = 0; x <= i; ++x) {
  26. f[i][j] = max(f[i][j], f[x][j - 1] + profit(prices, x + 1, i));
  27. }
  28. }
  29. }
  30. return f[n][k];
  31. }
  32. private:
  33. int profit(vector<int> &prices, int l, int u) {
  34. if (l >= u) return 0;
  35. int valley = INT_MAX;
  36. int profit_max = 0;
  37. for (int i = l - 1; i < u; ++i) {
  38. profit_max = max(profit_max, prices[i] - valley);
  39. valley = min(valley, prices[i]);
  40. }
  41. return profit_max;
  42. }
  43. };

Java

  1. class Solution {
  2. /**
  3. * @param k: An integer
  4. * @param prices: Given an integer array
  5. * @return: Maximum profit
  6. */
  7. public int maxProfit(int k, int[] prices) {
  8. if (prices == null || prices.length <= 1 || k <= 0) return 0;
  9. int n = prices.length;
  10. if (k >= n / 2) {
  11. int profit_max = 0;
  12. for (int i = 1; i < n; i++) {
  13. if (prices[i] - prices[i - 1] > 0) {
  14. profit_max += prices[i] - prices[i - 1];
  15. }
  16. }
  17. return profit_max;
  18. }
  19. int[][] f = new int[n + 1][k + 1];
  20. for (int j = 1; j <= k; j++) {
  21. for (int i = 1; i <= n; i++) {
  22. for (int x = 0; x <= i; x++) {
  23. f[i][j] = Math.max(f[i][j], f[x][j - 1] + profit(prices, x + 1, i));
  24. }
  25. }
  26. }
  27. return f[n][k];
  28. }
  29. private int profit(int[] prices, int l, int u) {
  30. if (l >= u) return 0;
  31. int valley = Integer.MAX_VALUE;
  32. int profit_max = 0;
  33. for (int i = l - 1; i < u; i++) {
  34. profit_max = Math.max(profit_max, prices[i] - valley);
  35. valley = Math.min(valley, prices[i]);
  36. }
  37. return profit_max;
  38. }
  39. };

源码分析

注意 Python 中的多维数组初始化方式,不可简单使用[[0] * k] * n], 具体原因是因为 Python 中的对象引用方式。可以优化的地方是 profit 方法及最内存循环。

复杂度分析

三重循环,时间复杂度近似为 O(n^2 \cdot k), 使用了 f 二维数组,空间复杂度为 O(n \cdot k).

Reference