通杀leetcode买卖股票系列

第一题、121. 买卖股票的最佳时机

题目

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

如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

注意:你不能在买入股票前卖出股票。

示例 1:

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

输出: 5

解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。

     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

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

输出: 0

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

思路

如果想要获得最大利润,我们就得寻找一个低点和一个高点,使它们得前后插值最大,那么我们假设在第i天卖出,同时我们记录前i-1天得最低值,遍历更新它们最大差值即可。

代码

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

第二题、122. 买卖股票的最佳时机 II

题目

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

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

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

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

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

思路

第二题变成了可以多次购买,那么想让利润最大那就是只去买涨得部分即可

代码

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

第三题、123. 买卖股票的最佳时机 III

题目

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

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

注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

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

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

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

思路

第三题在第二题的基础上加入了最多购买次数,可以最多购买2次。通过思考可以发现,在没有买股票的时候,对于每一天的选择是买或不买;在已经买了股票的时候,对于每一天的选择是卖或不卖。此时会发现这里的很像0-1背包问题,对于每一个物品,都有放和不放的选择,0-1背包是在状态转移中选择放或不放,同时对背包的容量有限制。已知的0-1背包状态转移方程为

$dp[i][v] = max(dp[i-1][v],dp[i-1][v-c[i]]+w[i])$

而这道题目比0-1背包多出一个是否持有股票的限制,(即,不能同时参与多笔交易),所以我们可以多定义一个状态,定义状态$dp[i][k][j]$表示在第i天,购买次数限制为k,目前的股票的持有状态为j的最大利润。

  • 在没有买股票的时候,对于每一天的选择是买或不买,即
    • 买 : $dp[i-1][k][0] - prices[i]$ ——> $dp[i][k+1][1]$
    • 不买:$dp[i-1][k][0]$ ——> $dp[i][k][0]$
  • 已经买了股票的时候,对于每一天的选择是卖或不卖,即
    • 卖:$dp[i-1][k][1] + prices[i]$ ——> $dp[i][k][0]$
    • 不卖:$dp[i-1][k][1]$ ——> $dp[i][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-1][0]-prices[i])$

最后考虑边界和初始化问题,在第一天时,只能选择买入,不能卖出。

代码

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

第四题、188. 买卖股票的最佳时机 IV

题目

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

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

注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [2,4,1], k = 2
输出: 2
解释: 在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
示例 2:

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

思路

当我拿到第四题时,我笑了,哈哈,这不和第3题差不多吗!改下交易次数不就OK!一提交,傻了,超出内存限制.......

好吧,那聪明的你想一想,是不是有一个0-1背包优化空间的方法,可以将二维数组变成滚动数组,它的状态转移方程为

$dp[j] = max(dp[j],dp[j-c[i]]+w[i])$

那么我们这道题也是可以优化的,优化后的状态转移方程为

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

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

我们知道,滚动数组最重要的枚举顺序问题,要利用倒叙枚举覆盖旧值。然而这道题正序倒序都可以,这个之后再说。

代码

class Solution {
public:
    int maxProfit(int num, vector<int>& prices) {
         int n = prices.size();
        if(n==0) return 0 ;
        int max_k = min(num,n/2);
        int dp[max_k+1][2];
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++)
        {
            for(int k = 1;k<=max_k;k++)
            {
                if(i==0)
                {
                    dp[k][0] = -prices[i];
                    dp[k][1] = 0;
                    continue;
                }
                dp[k][0] = max(dp[k-1][1]-prices[i],dp[k][0]);
                dp[k][1] = max(dp[k][0]+prices[i],dp[k][1]);
            }
        }
        int ans = dp[max_k][1];
        return ans;
    }
};

第五题、309. 最佳买卖股票时机含冷冻期

原文地址:https://www.cnblogs.com/simplekinght/p/13190016.html