【力扣】股票买卖系列问题的状态机解法

力扣股票系列题目汇总

贪心:

  • 121:最多进行一次买卖
  • 122:可以多次买卖

DP:

  • 309:多次买卖+卖出冷冻期
  • 714:多次买卖+卖出手续费
  • 123:最多两次买卖
  • 188:最多 k 次买卖

121 买卖股票的最佳时机

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

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

思路:贪心

要顺序地找到数组最小的元素,然后找到最大的元素,差值即为最大利润

代码

//时间复杂度O(n) 空间复杂度O(1)
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int buy = INT_MAX, sell = INT_MIN;
        for (int& price : prices){
            // 记录历史最低价格
            buy = min(buy, price);
            // 记录历史最大收益
            sell = max(sell, price - buy);
        }
        return sell;
    }
};

122 买卖股票的最佳时机 II

给定一个数组 prices ,其中 prices[i] 表示股票第 i 天的价格。

在每一天,你可能会决定购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以购买它,然后在 同一天 出售。
返回 你能获得的 最大 利润 。

思路:贪心

只要比前一天价格高,就卖出;卖出的同时买入,因为第二天有个min函数决定是否真的买入

代码

//时间复杂度O(n) 空间复杂度O(1)
class Solution {
public:
    // 只要比前一天高,就卖出
    int maxProfit(vector<int>& prices) {
        int buy = INT_MAX, profit = 0;
        for (int i = 0; i < prices.size(); ++i){
            buy = min(buy, prices[i]);
            if (prices[i] > buy) {
                profit += prices[i] - buy;
                buy = prices[i];
            }
        }
        return profit;
    }
};

优化

发现buyprices[i]的大小比较进行了两次,所以将循环体内的min函数去掉。

//时间复杂度O(n) 空间复杂度O(1)
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int profit = 0;
        for (int i = 1; i < prices.size(); ++i){  
            if (prices[i] > prices[i-1]) {
                profit += prices[i] - prices[i-1];
            }
        }
        return profit;
    }
};

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

给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。​

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

思路:状态机

维护四个数组,代表当天结束时,采用这种行为后,目前的收益
画出状态机(状态转移方程):
在这里插入图片描述

代码

//时间复杂度O(n) 空间复杂度O(n)
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        vector<int> buy(n), sell(n), s1(n), s2(n);
        buy[0] = s1[0] = - prices[0];
        s2[0] = sell[0] = 0;
        for (int i = 1; i < n; ++i){
            buy[i] = s2[i-1] - prices[i];
            s1[i] = max(s1[i-1], buy[i-1]);
            sell[i] = max(buy[i-1] + prices[i], s1[i-1] + prices[i]);
            s2[i] = max(s2[i-1], sell[i-1]);
        }
        return max(s2[n-1], sell[n-1]);
    }
};

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

给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

思路:状态机

考虑到交易费,不能无限次地买卖,每次想买入的时候都需要斟酌一下。所以每次sell之后,还可能是sell,即不买。由于没有冷冻期了,状态可以自旋了。
状态图:
在这里插入图片描述

代码

//时间复杂度O(n) 空间复杂度O(n)
class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int n = prices.size();
        vector<int> buy(n), sell(n);
        buy[0] = -prices[0], sell[0] = 0;
        for (int i = 1;i < n; ++i){
            buy[i] = max(buy[i-1], sell[i-1] - prices[i]);
            sell[i] = max(sell[i-1], buy[i-1] + prices[i] - fee);
        }
        return sell[n-1];
    }
};

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

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

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

思路

每次买卖都设置状态,因为不含冷冻期,状态还是可以自旋。
状态机:
在这里插入图片描述

代码

//时间复杂度O(n) 空间复杂度O(n)
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        vector<int> buy1(n), sell1(n), buy2(n), sell2(n);
        //buy2[0]不知道是多少,设置为最小值
        buy1[0] = - prices[0], buy2[0] = INT_MIN, sell1[0] = sell2[0] = 0;
        for (int i = 1; i < n; ++i){
            buy1[i] = max(buy1[i-1], -prices[i]);
            sell1[i] = max(sell1[i-1], buy1[i-1] + prices[i]);
            buy2[i] = max(buy2[i-1], sell1[i-1] - prices[i]);
            sell2[i] = max(sell2[i-1], buy2[i-1] + prices[i]);
        }         
        //可能完成一笔交易就是利润最大的
        return max(sell1[n-1], sell2[n-1]); 
    }
};

优化

由于每次状态转移时,dp数组不是全部都用上了,可以使用经典的DP空间优化术

//时间复杂度O(n) 空间复杂度O(1)
class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        if (n == 1) return 0;
        int buy1, buy2, sell1, sell2, prev_buy1 = - prices[0], prev_buy2 = INT_MIN, prev_sell1 = 0, prev_sell2 = 0;
        for (int i = 1; i < n; ++i){
            buy1 = max(prev_buy1, -prices[i]);
            sell1 = max(prev_sell1, buy1 + prices[i]);
            buy2 = max(prev_buy2, sell1 - prices[i]);
            sell2 = max(prev_sell2, buy2 + prices[i]);
            prev_buy1 = buy1;
            prev_buy2 = buy2;
            prev_sell1 = sell1;
            prev_sell2 = sell2;
        }         
        return max(sell1, sell2); 
    }
};

在这里插入图片描述

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

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

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

思路

根据123优化后的代码,把buysell 装进一个数组里面,例如buy = [buy0, buy1, ....]

代码

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        if (n <= 1 or k == 0) return 0;
        vector<int> buy(k), sell(k), prev_buy(k, INT_MIN), prev_sell(k);
        prev_buy[0] = -prices[0];
        for (int i = 1; i < n; ++i){
            for (int j = 0; j < k; ++j){
                if (j) buy[j] = max(prev_buy[j], sell[j-1]-prices[i]);      
                else buy[j] = max(prev_buy[j], -prices[i]);               
                sell[j] = max(prev_sell[j], buy[j] + prices[i]);   
                prev_buy[j] = buy[j];   
                prev_sell[j] = sell[j];    
            }
        }         
        return *max_element(sell.begin(), sell.end());; 
    }
};

在这里插入图片描述

总结

有冷冻期:状态机中增加状态
交易次数限制:状态不复用

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值