【LeetCode-188】买卖股票的最佳时机 IV

问题

给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例

输入: k = 2, prices = [2,4,1]
输出: 2
解释: 在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。

解答1:完整状态机

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        vector<vector<int>> hold(n + 1, vector<int>(k + 1, INT_MIN));
        vector<vector<int>> sold(n + 1, vector<int>(k + 1, 0));
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= k; j++) {
                sold[i][j] = max(sold[i - 1][j], hold[i - 1][j] + prices[i - 1]);
                hold[i][j] = max(hold[i - 1][j], sold[i - 1][j - 1] - prices[i - 1]);
            }
        }
        return sold[n][k];
    }
};

重点思路

本题一共有三个状态:天数、可购买次数、持有和未持有状态。首先不考虑状态压缩问题,直接写出三维dp数组dp[i][j][k]

接下来考虑状态转移方程问题,我们需要对每一个状态维度遍历所有可能取值,也就是需要三层for循环,但是“持有和未持有状态”只包含两个取值,所以可以直接拆开,最终只需要两个for循环。我们将这两个取值分别定义为hold(持有)和sold(未持有),具体的状态转移方程可写为:sold[i][j] = max(sold[i - 1][j], hold[i - 1][j] + prices[i])hold[i][j] = max(hold[i - 1][j], sold[i - 1][j - 1] - prices[i]);。为了避免复杂的初始化,我们在天数维度上考虑“第0天”。

然后是边界条件的初始化问题:

  • sold初始化:第0天必定是未持有状态,利润为0,并且后续未持有状态的最小利润必定大于0,所以可以将sold全部初始化为0;
  • hold初始化:第0天为hold的无效状态,并且后续需要求最大值,所以全部初始化为INT_MIN

解答2:状态压缩

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        vector<int> hold(k + 1, INT_MIN);
        vector<int> sold(k + 1, 0);
        for (int p : prices) {
            for (int i = 1; i <= k; i++) {
                sold[i] = max(sold[i], hold[i] + p);
                hold[i] = max(hold[i], sold[i - 1] - p);
            }
        }
        return sold[k];
    }
};

重点思路

根据解答1中的状态转移方程可知,天数这个维度每次都只考虑了上一天的值,所以这一个维度可以去掉。最终得到只包含两个一维dp数组的动态规划算法。

初始化方法同解答1。

posted @ 2021-04-03 15:48  tmpUser  阅读(53)  评论(0编辑  收藏  举报