买卖股票的最佳时机

买卖股票的最佳时机

题目表述

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

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

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

样例

Ex.1

输入:[7,1,5,3,6,4]

输出:5

解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

Ex.2

输入: prices = [7,6,4,3,1]

输出: 0

解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

题解

很简单,直接看代码吧。

代码

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        if(n<2) return 0;
        int buy = -prices[0], sell = 0;
        for(int i=1;i<prices.size();i++){
            buy = max(buy, -prices[i]);  // buy 始终是截止到第 i 天股票最便宜价格的相反数
            sell = max(sell, buy + prices[i]);  // 卖出,这里可以使用更新过后的buy 
        }

        return sell;
    }
};

买卖股票的最佳时机 II

题目描述看链接

题解

我们定义两个数组 b u y [ i ] , s e l l [ i ] buy[i], sell[i] buy[i],sell[i], 其中, 0 < = i < n 0 <=i<n 0<=i<n

b u y [ i ] buy[i] buy[i],表示在第i天且持有一只股票的前提下的最大收益。
s e l l [ i ] sell[i] sell[i],表示在第i天且手中没有股票的前提下的最大收益。

那么状态转移方程为:
b u y [ i ] = m a x ( b u y [ i − 1 ] , − p r i c e s [ i ] ) buy[i] = max(buy[i-1], -prices[i]) buy[i]=max(buy[i1],prices[i])
s e l l [ i ] = m a x ( s e l l [ i − 1 ] , b u y [ i ] + p r i c e s [ i ] ) sell[i] = max(sell[i-1], buy[i]+prices[i]) sell[i]=max(sell[i1],buy[i]+prices[i])
边界条件:
b u y [ 0 ] = − p r i c e s [ 0 ] s e l l [ 0 ] = 0 buy[0] = -prices[0] \\ sell[0] = 0 buy[0]=prices[0]sell[0]=0
刚开始,持有一只股票的话就是负收益。

代码

我们只需要从前一天的结果推导出后一天的结果。因此不需要使用数组来存储数据。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        if(n<2) return 0;
        int sell = 0;
        int buy = -prices[0];

        for(int i=1;i<n;i++){
            buy = max(buy, sell-prices[i]);
            sell = max(sell, buy+prices[i]);  //  因为我们不需要手续费,我们不需要保持buy是第i-1填的最优值。 假如在上一步buy被更新了,而在更新sell的时候又会加回来,还是等于sell。不会影响sell的更新。
        }

        return sell;
    }
};

买卖股票的最佳时机 III

题目看链接

题解

两种思路:

  1. 我们把数组分成两部分,一半求一个最优解。然后拼接一起。
    • 我们从前往后得到最好的只交易一次的数组 s e l l [ 0 − n ] sell[0-n] sell[0n]
    • 然后到这遍历数组得到最好的之交易一次的数组 l l e s [ 0 − n ] lles[0-n] lles[0n]
    • 最后我们遍历分割线找 max ⁡ { s e l l [ i ] + l l e s [ i ] } \max\{sell[i]+lles[i]\} max{sell[i]+lles[i]}
  2. 我们定义四种状态。卖了一只股的收益,卖了两只股的收益,手中持有一只股的收益,手中持有两只股的收益。

上面四种状态我们分别定义为 s e l l 0 , s e l l 1 , b u y 0 , b u y 1 sell_0, sell_1, buy_0, buy_1 sell0,sell1,buy0,buy1。我们始终保持上面四种状态的最大收益。

在第i天的状态。

  • 第一次买卖:
    • b u y 0 = m a x ( b u y 0 , − p r i c e s [ i ] ) buy_0 = max(buy_0, -prices[i]) buy0=max(buy0,prices[i]) # 始终为负数,因为还没买股票回本。始终保存最低的股价。
    • s e l l 0 = m a x ( s e l l 0 , b u y 0 + p r i c e s [ i ] ) sell_0 = max(sell_0, buy_0+prices[i]) sell0=max(sell0,buy0+prices[i]) # 将该股票卖了之后的收益。该值也是一次买卖的最优值。是问题1的结果。
  • 第二次买卖:
    • b u y 1 = m a x ( b u y 1 , s e l l 0 − p r i c e s [ i ] ) buy_1 = max(buy_1, sell_0-prices[i]) buy1=max(buy1,sell0prices[i]) # 第二次买股票是在第一次买卖之后的最大收益的基础上进行的。且始终保持买了第二只股票后收益最大。
    • s e l l 1 = m a x ( s e l l 1 , b u y 1 + p r i c e s [ i ] ) sell_1 = max(sell_1, buy_1+prices[i]) sell1=max(sell1,buy1+prices[i]) # 将第二只股票买了之后的最大收益。

代码

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        if(n<2) return 0;
        int sell[2], buy[2];
        sell[0] = 0, sell[1] = 0;
        buy[0] = -prices[0], buy[1] = -prices[0];
        for(int i=1;i<n;i++){
            buy[0] = max(buy[0], -prices[i]);      // 买第一只
            sell[0] = max(sell[0], buy[0]+prices[i]);      // 继续保持现在的金额,或是卖出仅有的股票
            // if(i>=3)
            buy[1] = max(buy[1], sell[0]-prices[i]);  // 买第二只
            // if(i>=3)
            sell[1] = max(sell[1], buy[1]+prices[i]);  // 买二只中的一只
        }
        return sell[1];
    }
};

买卖股票的最佳时机 IV

题目看链接

题解

按照第三题的方法2,我们只需保存一个2*k个买卖的状态就行。每次按顺序更新一下即可。

代码

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

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

题目看链接

题解

在含有冷冻期的情况下,我们就不能使用前一天的交易完的最优值,而是使用前两天交易完的的最优值去更新下一次买股票的最大收益。这就需要我们最初历史sell的值。

代码

class Solution {
public:  
    int maxProfit(vector<int>& prices) {
        int n = prices.size();
        if(n<2) return 0;
        int buy = -prices[0];
        vector<int> sell(n+1, 0);
        for(int i=1;i<n;i++){
            if(i-2<0)
                buy = max(buy, -prices[i]);
            else
                buy = max(buy, sell[i-2]-prices[i]);  //  含有冷工期,想要买这i填的股票。你需要在i-2天的完结交易状态的基础上进行。
            sell[i] = max(sell[i-1], buy+prices[i]);
        }

        return sell[n-1];
    }
};

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

题目看链接

题解

因为含有手续费,我们就不能把第二问的代码照搬过来。我们在根据前一天的最佳buy更新今天的最优buy时,需要把前一天的buy保存下来。
因为不含有手续费时,我们使用今天的buy进行更新sell会包含当天买卖的情况。但是保函手续费是,这种情况就包含不了了。

代码


class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int n = prices.size();
        if(n<2) return 0;
        int buy = -prices[0], sell = 0;

        for(int i=1;i<n;i++){
            int prebuy = buy;  // 前一天买股票的最大收益
            buy = max(buy, sell-prices[i]);  // 这一天买股票的最大收益
            sell = max(sell, prebuy+prices[i]-fee);  // 今天 卖出 前一天买过的股票 的最大收益
        }

        return sell;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值