买卖股票的最佳时机II

题目来源

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

题目描述

买卖股票的最佳时机 II 给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

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

输入: prices = [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格= 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 示例 3:

输入: prices = [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:
1 <= prices.length <= 3 * 1 0 4 10^{4} 104
0 <= prices[i] <= 1 0 4 10^{4} 104

题目分析

题目给了一串数组作为股票在一段时间内的价格,看似教我们赚钱,实际上同时只能买一支股票,那么无非就是低买高卖。买卖次数不限,让我们找出这段时间能赚的最多的买卖方法。这其中需要考虑的问题就是时机。

解决办法

动态规划

动态规划就是先假设只有两三个数字(最简单的情况),先算出不同的状态的最大值,然后一个一个向模型中加入数字,利用之前的计算结果慢慢地推出最后的结果。
通常我们使用一个很大的数组,然后按照遍历一步步算出dp[i]的值,前面的结果往往会对后面产生指导作用。
我们知道,在这个游戏中的小人有两种状态,

  1. 没买股票
  2. 买了股票

如果我们把0和1当成一次操作,可以发现,对于任意的数列,我们有:

  • 当0→1时,买入了股票,没有收入。
  • 当0→0时,无事发生,没有收入。
  • 当1→0时,卖出了股票,有收入。
  • 当1→1时,不可能同时买入两支股票。

这个时候我们可以建立二维数组 d p [ p r i c e S i z e ] [ 2 ] dp[priceSize][2] dp[priceSize][2]模拟每种情况下的最大收入,其中第一维度指代模型中最后一个数据的下标,第二维表示两种状态。

    int dp[pricesSize][2];
    dp[0][0]=0;dp[0][1]=0;

我们首先把第一个数据 p r i c e s [ 0 ] prices[0] prices[0]放入动态模型,当我们没有买它,我们得到0元;买了,同样得到0元(因为我们只有一个数据,还没有卖出)。
从第二个数据开始,我们可以得到:
d p [ i ] [ 1 ] = d p [ i − 1 ] [ 0 ] ; dp[i][1]=dp[i-1][0]; dp[i][1]=dp[i1][0];
如果我们准备在这次买下股票,那么我们最大的收入就是上一次没有买股票时收入最大值 d p [ i − 1 ] [ 0 ] dp[i-1][0] dp[i1][0](因为上一次买了这一次就不能买)。
而如果我们这次没有买股票,我们可以选择:

  • 上次没买的最大收入 d p [ i − 1 ] [ 0 ] dp[i-1][0] dp[i1][0](两次都没买);
  • 从之前买进的某一次股票开始卖出 p r i c e s [ i ] − p r i c e s [ j ] + d p [ j ] [ 1 ] prices[i]-prices[j]+dp[j][1] prices[i]prices[j]+dp[j][1]

第二个选择可能有点抽象,我们知道如果一直持有股票没有卖的话,这期间是没有收入的,所以直接记上次买了之后的最大收入,加上这次交易的收入,因为我们有很多的选择(买之前的任何股票再卖),这里需要一个遍历。

 for(i=1;i<pricesSize;i++)
    {
        dp[i][1]=dp[i-1][0];
        int max=dp[i-1][0];
        for(j=0;j<i;j++) if(max<prices[i]-prices[j]+dp[j][1]) 
            max=prices[i]-prices[j]+dp[j][1];
        dp[i][0]=max;
    }

这就是状态转移方程,我们不考虑之前到底是怎样买卖,只留下了最大利润的数字,有了动态规划的数组之后,我们只需要考虑最后一次交易,这就是一个纯数据的比较。

总的函数代码如下

int maxProfit(int* prices, int pricesSize){
    if(pricesSize==1) return 0;
    //状态转移方程的动态规划
    int dp[pricesSize][2];
    int i,j;
    //0没买1买了
    dp[0][0]=0;dp[0][1]=0;
    for(i=1;i<pricesSize;i++)
    {
        dp[i][1]=dp[i-1][0];
        int max=dp[i-1][0];
        for(j=0;j<i;j++) if(max<prices[i]-prices[j]+dp[j][1]) 
            max=prices[i]-prices[j]+dp[j][1];
        dp[i][0]=max;
    }
    return dp[pricesSize-1][0];
}

结果判定:
动态规划得分

设置哨兵

我发现了滑点(不是) 想了想,在股票世界里就是起起伏伏,既然我们可以不断买卖,而且还预知未来,为什么不把每一次“起”都收入囊中呢,只需要避开所有的“跌”就不会亏呀。
买卖示意图
如上图所示,只要在所有的上行线上都保持“持有”的状态,就可以稳定增值,即:最低点买入,最高点卖出
代码实现的时候有几个注意点,如何判断是最低/最高点?
这个时候就一直用选择判断筛选。我们可以一直保持一个较低的点,作为 p h e a d phead phead,一直低就一直更新它的值。如果股票进入“上升期”,就可以更新最高点 p t a i l ptail ptail的值,直到又下降,可以知道刚刚已经经过了最高点,可以在上一次卖出(典型的马后炮)。

 if(phead>prices[i]&&ptail==-1) phead=prices[i];
            else if(ptail==-1||ptail<prices[i]) ptail =prices[i];
            else{
                maxp+=ptail-phead;
                phead=prices[i];
                ptail=-1;
            }

此处我使用了 p t a i l = − 1 ptail=-1 ptail=1这样的初始值,来判定是否存在峰值,我们知道,一次结算之后,这个最高点已经用过了,需要重置。而 p h e a d phead phead是沿用了“判定后的第一个数”作为暂时的低点。
这时会出现一个问题,我们等到一条线下降了才知道最高点出现,但是如果最后一段一直是上涨阶段,直到“涨没了”(数组尽头),我们手里还有一支没有抛售的股票,怎么办呢?
于是,设置数组尽头的检查,如果最后成为了较高点,就更新 p t a i l ptail ptail并且进行结算,此处没有再重置 p t a i l ptail ptail的值是因为遍历已经结束了。

if(i==pricesSize-1&&prices[i]>phead)
        {
            if(prices[i]>ptail) ptail=prices[i];
            maxp+=ptail-phead;
        } 

总的函数代码如下:

int maxProfit(int* prices, int pricesSize){
    if(pricesSize==1) return 0;
    //如果说股市是VVV形,只需要在最低点买,在最高点卖
    int phead = prices[0],ptail=-1,maxp=0;
    int i;
    for(i=1;i<pricesSize;i++)
    {
        if(i==pricesSize-1&&prices[i]>phead)
        {
            if(prices[i]>ptail) ptail=prices[i];
            maxp+=ptail-phead;
        } 
        else{
            if(phead>prices[i]&&ptail==-1) phead=prices[i];
            else if(ptail==-1||ptail<prices[i]) ptail =prices[i];
            else{
                maxp+=ptail-phead;
                phead=prices[i];
                ptail=-1;
            }
        }
    }
    return maxp;

}

结果判定:
哨兵得分

总结

我认为第二个方法比较好理解,但是它只是一个“特解”,所以又写下了动态规划的解法,可以帮助解决更多类型的问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值