【Leetcode】188. Best Time to Buy and Sell Stock IV

题目地址:

https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/

给定每天的股票价格,以数组 p p p的形式给出,每天只能做一次交易并且每次只能买一股,问如果只允许做最多 k k k次买卖,那么最大收益是多少。

不妨设 p p p的长度为 n n n。首先,如果 k ≥ n / 2 k\ge n/2 kn/2,那么就相当于可以买卖无限次,因为买入和卖出的总次数小于等于 n n n。而买卖无限次是很容易的,只需要将每次股票上涨的差价都赚到即可。接下来考虑 k < n / 2 k< n/2 k<n/2的情况。

思路是状态机。规定两个状态,状态 1 1 1表示手中有股, 0 0 0表示手中无股。那么:
1、状态 1 1 1可以由状态 1 1 1达到,即昨天手里有股,然后持有一天到今天仍然有股,则边权为 0 0 0;状态 1 1 1可以由状态 0 0 0达到,即昨天手里无股,然后今天买入,状态变为有股,则边权为 − p [ i ] -p[i] p[i]
2、状态 0 0 0可以由状态 0 0 0达到,即昨天手里无股,然后观望一天,今天仍然无股,则边权为 0 0 0;状态 0 0 0可以由状态 1 1 1达到,即昨天手里有股,然后今天卖出,状态变为无股,则边权为 + p [ i ] +p[i] +p[i]
而题目相当于是要问,最多允许从两个状态转 k k k圈(表示买入 k k k次卖出 k k k次)的情况下,最大收益是多少。所以只需要将交易次数加入状态计算中即可。设 f [ i ] [ j ] f[i][j] f[i][j]表示以状态 1 1 1结尾的,到第 i i i天为止的(这里 i i i 0 0 0计数),已经执行过最多 j j j次买入操作的情况下的最大收益, g [ i ] [ j ] g[i][j] g[i][j]表示以状态 0 0 0结尾的,到第 i i i天为止的(这里 i i i 0 0 0计数),已经执行过最多 j j j次买入操作的情况下的最大收益。则: f [ i ] [ j ] = max ⁡ { f [ i − 1 ] [ j ] , g [ i − 1 ] [ j − 1 ] − p [ i ] } g [ i ] [ j ] = max ⁡ { g [ i − 1 ] [ j ] , f [ i − 1 ] [ j ] + p [ i ] } f[i][j]=\max\{f[i-1][j],g[i-1][j-1]-p[i]\}\\g[i][j]=\max\{g[i-1][j],f[i-1][j]+p[i]\} f[i][j]=max{f[i1][j],g[i1][j1]p[i]}g[i][j]=max{g[i1][j],f[i1][j]+p[i]}那么要返回的就是 g [ n − 1 ] [ k ] g[n-1][k] g[n1][k](原因是题目隐含要求是要从cash角度考虑,即要以无股的状态结尾,也就是股票不能持有到最后一天仍然不卖)。初始条件,首先我们知道 f [ 0 ] [ 1 ] = . . . = f [ 0 ] [ k ] = − p [ 0 ] f[0][1]=...=f[0][k]=-p[0] f[0][1]=...=f[0][k]=p[0],这个表示如果第 0 0 0天就是以有股结尾,那么那天的cash净值当然是负的股价。由于 f [ 0 ] [ 0 ] f[0][0] f[0][0]这个状态是非法的(因为不可能经过了 0 0 0次买入操作却有股),为了使得这个状态转移到的答案一定被排除掉,我们令 f [ 0 ] [ 0 ] = − ∞ f[0][0]=-\infty f[0][0]=。代码如下:

class Solution {
 public:
  int maxProfit(int k, vector<int>& ps) {
    int n = ps.size();
    int res = 0;
    if (k >= n / 2) {
      for (int i = 1; i < n; i++) res += max(ps[i] - ps[i - 1], 0);
      return res;
    }

    int buy[n][k + 1], sell[n][k + 1];
    for (int i = 1; i <= k; i++) buy[0][i] = -ps[0];
    buy[0][0] = -2e9;
    memset(sell, 0, sizeof sell);
    for (int i = 1; i < n; i++)
      for (int j = 0; j <= k; j++) {
        buy[i][j] = buy[i - 1][j];
        if (j) buy[i][j] = max(buy[i][j], sell[i - 1][j - 1] - ps[i]);
        sell[i][j] = max(sell[i - 1][j], buy[i - 1][j] + ps[i]);
      }

    return sell[n - 1][k];
  }
};

时空复杂度 O ( n k ) O(nk) O(nk)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值