(如何构建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];
}
};