LeetCode--Best Time to Buy and Sell Stock IV(DP + 滚动数组)

36 篇文章 0 订阅
1 篇文章 0 订阅

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

题意:已知第1天、第2天......第N天的股票价格,每天只能进行一次买或者卖,且规定手里的股票卖出之前不能买进新的股票,问在最多进行K次交易的情况下,采用最优方案净利润能有多少。

分析:

1、设j天完成不超过i次交易能得到的最大收益是f(i,j),显然f(0,0) = 0,f(i, 1) = 0(因为只有1天不能完成任何交易)

2、对最后一天也就是第j天发生的情况进行分类,第j天可能没有买卖,也可能最后一次卖出手里的股票,即

      f(i, j) = max{ f(i-1, j), f(i, j-1), f(i-1,k-1) + a[j] - a[k] },其中k >= 1 && k < j,即最后一天卖出的股票可能是第1天、第2天......第j-1天买入的股票

3、状态方程有了,似乎可以直接写了,但由于k的存在,这一算法的复杂度是O(k*n*n)的,在大数据情况下会TLE,我们看看能不能对后面这个f(i-1,k-1) + a[j] - a[k]做一些分析

4、我们令g(i, j) = f(i-1, k-1) - a[k],其中k >= 1 && k < j,那它表示什么意思呢,即最后第j天之前,最多进行了i-1次交易,且最后一次买入发生在第k天,这一过程之后手里money最多还有多少(或最少还欠多少),显然g(i, 1) = 0(因为第1天之前最多进行0次交易),g(i, 2) = f(i-1, 0) - a[1],将k带入,我们可以看到:

g(i, j) = max{ f(i-1, 0) - a[1], f(i-1, 1) - a[2], ..., f(i-1, j-2) - a[j-1] }

g(i, j+1) = max{ f(i-1, 0) - a[1], f(i-1, 1) - a[2], ..., f(i-1, j-2) - a[j-1], f(i-1, j-1) - a[j] }

               = max{ g(i, j), f(i-1, j-1) - a[j] }

即g(i, j)的递推会用到f(i, j)的状态

5、将g(i, j)带入我们的状态方程,则 

g(i, j) = max{ g(i, j-1), f(i-1, j-2) - a[j-1] }

f(i, j) = max{  f(i-1, j), f(i, j-1), g(i, j) + a[j] }

可以看到g的当前状态仅和本层前一个状态、f的上一层前一个状态有关,f当前状态仅和上一层的当前状态、本层的前一个状态、g的当前状态有关,因此我们可以采用滚动数组的形式,仅保留2层状态,即能完成状态转移的过程

6、最后还有一个优化(有点像多重背包可以分成完全背包和0-1背包):

当2k >= n时,即我们可以做到在任意一天买入任意之后一天卖出,在直方图中容易看出这相当于只要有利可图我们就能获得


class Solution {
public:
    int allProfit(vector<int>& prices){
        int tot = 0;
        for(int i = 1; i < prices.size(); ++i){
            tot += max(0, prices[i] - prices[i-1]);
        }
        return tot;
    }
    int maxProfit(int k, vector<int> &prices) {
        int n = prices.size();
        if(k * 2 >= n) return allProfit(prices);
        
        vector<int> mem((n + 1) * 2, 0);
        int *pref = &mem[0], *curf = &mem[n+1], g, *a = &prices[0] - 1;
        for(int i = 1; i <= k; ++i){
            //递推第1天和第2天的情况,因为比较特殊
            curf[1] = 0;                  //g[1] = 0, g[2] = pref[0] - a[1]
            curf[2] = max(0, a[2] - a[1]);  g = -a[1];
            //递推之后的情况
            for(int j = 3; j <= n; ++j){
                g = max(g, pref[j-2] - a[j-1]);
                curf[j] = max(max(pref[j], curf[j-1]), g + a[j]);
            }
            swap(pref, curf);
        }
        return pref[n];
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值