leetcode 困难 —— 买卖股票的最佳时机 IV(详细思路,很妙的dp题)

(如何构建dp数组,状态转移方程建立,还有 边缘考虑 都比较复杂,很妙的题)

题目:
给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

题解:

假设 prices 数组的长度为 n
首先很容易想到我们可以 dp[k][n] 构造一个 dp
dp[i][j] 的含义为 最多完成 i 笔交易的情况下,在 0…j 天期间,能获得的最大利润

我们很容易求到 x~y 天,最多只有一笔交易时,最大的利润

那么我们就可以通过预处理,容易得到 value[i][j] 为 i~j 天,最多只有一笔交易时的最大利润

这么一来,我们可以借助 dp[i][j] = max(dp[i - 1][w] + value[w + 1][j]),0 <= w <= j
但是这样的复杂度就很高了,k*(n^2)

考虑整个过程应该怎么优化,虽然我们每次都重复获取了 dp[i - 1][w],0 <= w <= j
但是,好像并没有什么方法去避免,因为value[w+1][j] 都是不同的
这样的话,这个状态转移就一直没想到方法优化


既然状态转移想不出来优化的方法
那只能从别的优化方向下手

比如重新考虑如何构建dp数组
我们需要两个数组,buy[k][n] 和 sell[k][n]

sell[i][j] 即前 j 天,最多有 i 笔交易,但目前并不在交易中
buy[i][j] 即前 j 天,最多有 i 笔交易,且目前正在交易中(正在交易的也算进 i 中)

考虑状态转移

sell[i][j] = max(sell[i][j - 1], buy[i][j - 1] + prices[j])
第一种情况:第 j 天没有卖出股票
所以直接等于 sell[i][j - 1] 即可
第二种情况:第 j 天卖出了正在交易的股票
注意,因为正在交易也算进 i 中,所以基于的是 buy[i][j - 1] 而不是 buy[i - 1][j - 1]

buy[i][j] = max(buy[i][j - 1], sell[i - 1][j - 1] - prices[j])
第一种情况:第 j 天没有买入股票
那就是以前买的股票,直接等于 buy[i][j - 1]
第二种情况:第 j 天买入了股票
这种,就相当于 j - 1 天时,只交易了 i - 1 笔,所以基于的是 sell[i - 1][j - 1]

考虑完状态转移之后,还有个重要的部分,边缘怎么考虑

i = 0 的时候怎么考虑
sell[0][j] = 0,因为没有交易,所以身上钱肯定都是 0
buy[0][j] = INT_MIN + 1005,因为这是错误情况,buy 是目前正在有交易,但是 k 又是 0,矛盾了

还有就是 j = 0 的时候怎么考虑
sell[i][0] = 0,就第一天,能赚什么钱,最多也就不买或者买入又卖出,都是 0
buy[i][0] = -prices[0],因为当前正在交易,所以第一个一定要买入

接下来代码就简单了
代码如下

class Solution {
public:
    int buy[105][1005];
    int sell[105][1005];
    int maxProfit(int k, vector<int>& prices) {
        for(int j = 0; j < prices.size(); j++) {
            sell[0][j] = 0;
            buy[0][j] = INT_MIN + 1005;
        }
        for(int i = 1; i <= k; i++) {
            sell[i][0] = 0;
            buy[i][0] = -prices[0];
            for(int j = 1; j < prices.size(); j++) {
                sell[i][j] = max(sell[i][j - 1], buy[i][j - 1] + prices[j]);
                buy[i][j] = max(buy[i][j - 1], sell[i - 1][j - 1] - prices[j]);
            }
        }
        return sell[k][prices.size() - 1];
    }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值