代码随想录算法训练营 | 动态规划 part08

121. 买卖股票的最佳时机

121. 买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

在这里插入图片描述
贪心
只能买卖一次
在某一天买入:希望在股票价格最低的一天买入;
未来的某一个不同的日子 卖出:在未来的价格最高的一天卖出;
如此,获得的利润最大;

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int res = 0;
        int minPirce = prices[0];
        for (int i = 1; i < prices.size(); i++) {
            res = max(res, prices[i] - minPirce);
            minPirce = min(minPirce, prices[i]);
        }
        return res;
    }
};

动态规划
n 为表示股票价格数组的长度;
i 表示第 i 天(i0 开始到 n - 1);
k 表示允许(交易)的 最多 次数;
dp[i][k] 表示在第 i 天 最多 进行 k 笔交易获得的最大利润。
初始条件:dp[-1][k] = dp[i][0] = 0dp[-1][k]这里第一天对应 i = 0,所以第 -1 天没有股票交易;dp[i][0]即如果没有交易,就不产生任何利润;
i 天可以:买入、卖出、不进行任何操作;目的是使最终利润最大;
本题只能交易一次;意味着全程最多持有一支股票;假设在第 i 天买入股票,则买入之前持有 0 支股票;假设在第 i 天卖出股票,则卖出之前正好持有 1 支股票;第 i 天持有几只股票会影响我们在第 i 天的操作,继而影响最大利润;
综上所述,对 dp[i][k] 的应该分为两个部分:dp[i][k][0]dp[i][k][1]
dp[i][k][0] 表示第 i 天进行最多进行 k 笔交易并且完成以后,我们当前不持股而获得的利润;
dp[i][k][1] 表示第 i 天进行最多进行 k 笔交易并且完成以后,我们当前持股而获得的利润;
base cases:

dp[-1][k][0] = 0; // -1天
dp[-1][k][1] = -Infinity; // 没有进行股票交易时不允许持有股票

dp[i][0][0] = 0; // 没有交易,没有利润
dp[i][0][1] = -Infinity; // 没有进行股票交易时不允许持有股票

状态转移方程:

dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]);
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i]);

dp[i][k][0]i 天只能是无任何操作或卖出;当第 i 天无任何操作时,当天持股为0,则第 i - 1 天所有交易完成后,为不持股状态dp[i - 1][k][0],即为无操作的最大利润;当第 i 天卖出时,卖出之前持股为1,则第 i - 1 天所有交易完成后,为持股状态dp[i - 1][k][1],卖出的最大利润为第 i - 1 天持股的最大利润加上第 i 天卖出股票的金额;
dp[i][k][1]i 天只能是无任何操作或买入;当第 i 天无任何操作时,当天持股为1,则第 i - 1 天所有交易完成后,为持股状态dp[i - 1][k][1],即为无操作的最大利润;当第 i 天买入时,买入之前持股为0,则第 i - 1 天所有交易完成后,为持股状态dp[i - 1][k - 1][0](这里最大交易次数减少一次,是因为买入操作会使用一次交易(买入-卖出为一次交易)),买入的最大利润为第 i - 1 天持股的最大利润减去第 i 天买入股票的金额;
最终的最大利润是 dp[n - 1][k][0],因为结束时持有 0 份股票的收益一定大于持有 1 份股票的收益。

回到本题:k = 1
状态转移方程:

dp[i][1][0] = max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i]);
dp[i][1][1] = max(dp[i - 1][1][1], dp[i - 1][0][0] - prices[i]) = max(dp[i - 1][1][1], - prices[i]);

因为dp[i][0][0] = 0;
优化为二维数组:

dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
dp[i][1] = max(dp[i - 1][1], - prices[i]);
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<vector<int>> dp(prices.size(), vector<int>(2));
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        for (int i = 1; i < prices.size(); i++) {
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            dp[i][1] = max(dp[i - 1][1], -prices[i]);
        }
        return dp[prices.size() - 1][0];
    }
};

i 天的最大利润只和第 i - 1 天的最大利润相关,也可以继续优化为常数级

122.买卖股票的最佳时机II

122.买卖股票的最佳时机II

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。

可以买卖多次
贪心

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int res = 0;
        for (int i = 0; i < prices.size() - 1; i++) {
            int diff = prices[i + 1] - prices[i];
            if (diff > 0) {
                res += diff;
            }
        }
        return res;
    }
};

动态规划
本题:k = +Infinity,同时最多可以持有一支股票;

dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]);
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i]);

因为k = +Infinity,所以 k 和 k - 1 可以看成相同的;

dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]);
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k][0] - prices[i]);

优化成二维数组

dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<vector<int>> dp(prices.size(), vector<int>(2));
        dp[0][0] = 0;
        dp[0][1] = -prices[0];
        for (int i = 1; i < prices.size(); i++) {
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][0]-prices[i]);
        }
        return dp[prices.size() - 1][0];
    }
};

i 天的最大利润只和第 i - 1 天的最大利润相关,也可以继续优化为常数级

123.买卖股票的最佳时机III

123.买卖股票的最佳时机III

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

本题:最多买卖两次,k = 2

dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]);
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i]);

因为k = 2,所以有:

dp[i][1][0] = max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i]);
dp[i][1][1] = max(dp[i - 1][1][1], dp[i - 1][0][0] - prices[i]) = max(dp[i - 1][1][1], - prices[i]);
dp[i][2][0] = max(dp[i - 1][2][0], dp[i - 1][2][1] + prices[i]);
dp[i][2][1] = max(dp[i - 1][2][1], dp[i - 1][1][0] - prices[i]);
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        vector<vector<vector<int>>> dp(prices.size(), vector<vector<int>>(3, vector<int>(2)));
        dp[0][1][0] = 0;
        dp[0][1][1] = -prices[0];
        dp[0][2][0] = 0;
        dp[0][2][1] = -prices[0];
        for (int i = 1; i < prices.size(); i++) {
            dp[i][1][0] = max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i]);
            dp[i][1][1] = max(dp[i - 1][1][1], - prices[i]);
            dp[i][2][0] = max(dp[i - 1][2][0], dp[i - 1][2][1] + prices[i]);
            dp[i][2][1] = max(dp[i - 1][2][1], dp[i - 1][1][0] - prices[i]);
        }
        return dp[prices.size() - 1][2][0];
    }
};

i 天的最大利润只和第 i - 1 天的最大利润相关,也可以继续优化为常数级

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int profitOne0 = 0;
        int profitOne1 = -prices[0];
        int profitTwo0 = 0;
        int profitTwo1 = -prices[0];
        for (int i = 1; i < prices.size(); i++) {
            profitOne0 = max(profitOne0, profitOne1 + prices[i]);
            profitOne1 = max(profitOne1, - prices[i]);
            profitTwo0 = max(profitTwo0, profitTwo1 + prices[i]);
            profitTwo1 = max(profitTwo1, profitOne0 - prices[i]);
        }
        return profitTwo0;
    }
};
  • 26
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值