LeetCode-买卖股票问题

股票题

前言

在这里插入图片描述

此题是k = 2

利用「状态」进行穷举。我们具体到每一天,看看总共有几种可能的「状态」,再找出每个「状态」对应的「选择. 我们要穷举所有「状态」,穷举的目的是根据对应的「选择」更新状态

for 状态1 in 状态1的所有取值:
    for 状态2 in 状态2的所有取值:
        for ...
            dp[状态1][状态2][...] = 择优(选择1,选择2...)

每天都有三种「选择」:买入、卖出、无操作,我们用 buy, sell, rest 表示这三种选择

并不是每天都可以任意选择这三种选择的,因为 sell 必须在 buy 之后,buy 必须在 sell 之后

rest 操作还应该分两种状态,一种是 buy 之后的 rest(持有了股票),一种是 sell 之后的 rest(没有持有股票)

我们还有交易次数 k 的限制,就是说你 buy 还只能在 k > 0 的前提下操作

dp[3][2][1] 的含义就是:今天是第三天,我现在手上持有着股票,至今最多进行 2 次交易。再比如 dp[2][3][0] 的含义:今天是第二天,我现在手上没有持有股票,至今最多进行 3 次交易。很容易理解,对吧?

状态转移框架

在这里插入图片描述
状态转移方程:

dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
              max(   选择 rest  ,             选择 sell      )
​解释:今天我没有持有股票,有两种可能:
要么是我昨天就没有持有,然后今天选择 rest,所以我今天还是没有持有;
要么是我昨天持有股票,但是今天我 sell 了,所以我今天没有持有股票了。
​dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
              max(   选择 rest  ,           选择 buy         )
​解释:今天我持有着股票,有两种可能:
要么我昨天就持有着股票,然后今天选择 rest,所以我今天还持有着股票;
要么我昨天本没有持有,但今天我选择 buy,所以今天我就持有股票了。

如果 buy,就要从利润中减去 prices[i],如果 sell,就要给利润增加 prices[i]


定义base case
dp[-1][k][0] = 0
解释:因为 i 是从 0 开始的,所以 i = -1 意味着还没有开始,这时候的利润当然是 0 。
dp[-1][k][1] = -infinity
解释:还没开始的时候,是不可能持有股票的,用负无穷表示这种不可能。
dp[i][0][0] = 0
解释:因为 k 是从 1 开始的,所以 k = 0 意味着根本不允许交易,这时候利润当然是 0 。
dp[i][0][1] = -infinity
解释:不允许交易的情况下,是不可能持有股票的,用负无穷表示这种不可能。

总结
base case:
dp[-1][k][0] = dp[i][0][0] = 0
dp[-1][k][1] = 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])

121. 买卖股票的最佳时机

在这里插入图片描述
思路
此时K == 1 可以先忽略不计
dp[i][0]:表示第i天没有持有股票 : 1.保持前一天没持有; 2.昨天持有,但是卖了
dp[i][1]:表示第i天有持有 : 1.他保持前一天持有; 2.今天买了

int maxProfit(vector<int>& prices) {
        if (prices.size() < 2) return 0;
        int dp[prices.size()][2];
        for (int i = 0; i < prices.size(); i++) {
            if (i - 1 == -1) {
                dp[i][1] = -prices[i];
                dp[i][0] = 0;
                continue;
            }
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            //因为只买卖一次 K=1的情况下, dp[i][1] 不需要考虑当前余额,如果需要就要dp[i - 1][0]  -prices[i]
            dp[i][1] = max(dp[i - 1][1], -prices[i]);
        }
        return dp[prices.size() - 1][0];
    }

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

在这里插入图片描述

思路
与上题相比, k是无限,可以多次交易

 int maxProfit(vector<int>& prices) {
        if (prices.size() < 2) return 0;
        int dp[prices.size()][2];
        for (int i = 0; i < prices.size(); i++) {
            if (i - 1 == -1) {
                dp[0][0] = 0;
                dp[0][1] = -prices[i];
                continue;
            }
            //因为是多次交易,所以需要保存temp, temp是昨天为持有的收益,可能是已经卖了
            int temp = dp[i - 1][0];
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            dp[i][1] = max(dp[i - 1][1], temp - prices[i]);
        }
        return dp[prices.size() - 1][0];
    }

714. 买卖股票的最佳时机含手续费

在这里插入图片描述

思路
就是在卖出的时候加上手续费就行

int maxProfit(vector<int>& prices, int fee) {
        if (prices.size() < 2) return 0;
        int dp[prices.size()][2];
        for (int i = 0; i < prices.size(); i++) {
            if (i == 0) {
                dp[0][0] = 0;
                dp[0][1] = -prices[0];
                continue;
            }
            int temp = dp[i - 1][0];
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee);
            dp[i][1] = max(dp[i - 1][1], temp - prices[i]);
        }
        return dp[prices.size() - 1][0];
    }

309. 最佳买卖股票时机含冷冻期

在这里插入图片描述

思路
就是买的话是从[i - 2] 开始

//f1 因为可能冻结期有2 3 4天, 此方法不靠谱
int maxProfit(vector<int>& prices) {
        if (prices.size() < 2) return 0;
        int dp[prices.size()][2];
        for (int i = 0; i < prices.size(); i++) {
            if (i == 0) {
                dp[0][0] = 0;
                dp[0][1] = -prices[0];
                continue;
            }
            if (i == 1) {
                dp[1][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
                dp[1][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
                continue;
            }
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            dp[i][1] = max(dp[i - 1][1], dp[i - 2][0] - prices[i]);
        }
        return dp[prices.size() - 1][0];
    }
    
//f2
int maxProfit(vector<int>& prices) {
        if (prices.size() < 2) return 0;
        //前两天未持有
        int pre2_i_0 = 0;
        //第i天未持有
        int dp_i_0 = 0;
        //第i天持有
        int dp_i_1 = INT_MIN;
        for (int i = 0; i < prices.size(); i++) {
            int temp = dp_i_0;
            dp_i_0 = max(dp_i_0, dp_i_1 + prices[i]);
            dp_i_1 = max(dp_i_1, pre2_i_0 - prices[i]);
            pre2_i_0 = temp;
        }
        return dp_i_0;
    }

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

在这里插入图片描述

思路
K == 2了, 需要创建一个三维的dp

int maxProfit(vector<int>& prices) {
        if (prices.size() < 2) return 0;
        int max_k = 2;
        int dp[prices.size()][max_k + 1][2];
        //初始化这块内存区域,全部设置为0
        memset(dp, 0, sizeof(dp));
        for (int i = 0; i < prices.size(); i++) {
            for (int k = max_k; k >= 1; k--) {
                if (i - 1 == -1) {
                    dp[0][k][0] = 0;
                    dp[0][k][1] = -prices[0];
                    continue;
                }
                //dp[i][k][0] 第i天还有k次交易机会没持有: 没持有原因是 保持昨天或者昨天买今天卖
                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]);
            }
        }
        return dp[prices.size() - 1][max_k][0];
    }

188. 买卖股票的最佳时机 IV

在这里插入图片描述
思路
和上题k = 2 一样, 只不过这次k > 2, 那么因为股票买进卖出最多是 prices.size / 2次, 大于的话就相当于k=无限
那么就可以忽略k了

//k为不限制的function
int moewMaxProfit(vector<int>& prices) {
        if (prices.size() < 2) return 0;
        int dp[prices.size()][2];
        for (int i = 0; i < prices.size(); i++) {
            if (i - 1 == -1) {
                dp[0][0] = 0;
                dp[0][1] = -prices[i];
                continue;
            }
            //因为是多次交易,所以需要保存temp, temp是昨天为持有的收益,可能是已经卖了
            int temp = dp[i - 1][0];
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
            dp[i][1] = max(dp[i - 1][1], temp - prices[i]);
        }
        return dp[prices.size() - 1][0];
    }
	
    int maxProfit(int k, vector<int>& prices) {
        if (prices.size() < 2) return 0;
        //如果k > 一半的话就是无限的
        if (k > prices.size() / 2) {
            return moewMaxProfit(prices);
        }
                int max_k = k;
                int dp[prices.size()][max_k + 1][2];
                memset(dp, 0, sizeof(dp));
                for (int i = 0; i < prices.size(); i++) {
                    for (int k = max_k; k >= 1; k--) {
                        if (i - 1 == -1) {
                            dp[0][k][0] = 0;
                            dp[0][k][1] = -prices[0];
                            continue;
                        }
                        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]);
                    }
                }
        return dp[prices.size() - 1][max_k][0];
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值