leetcode 121. 买卖股票的最佳时机I,II,III,IV

题目I:买卖一次

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。
注意:你不能在买入股票前卖出股票。
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

分析

方法1

动态规划

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size()<=1) return 0;
        //使用前缀和数组,每次都找自己左边的最小值
        int res = 0;
        int max_v = *max_element(prices.begin(), prices.end());
        vector<int> dp(prices.size(), max_v+1);
        if(prices[0]<prices[1]) dp[1] = prices[0];
        for(int i=2;i<prices.size();i++){
            if(prices[i-1]<dp[i-1]) dp[i] = prices[i-1];
            else dp[i] = dp[i-1];
        }    
        for(int i=0;i<dp.size();i++){
            res = max(res, prices[i]-dp[i]);
        }
        return res;
    }
};
方法2

改进空间的动态规划。
由于当前状态dp[i]只与前一个状态相关,因此可以不用使用数组将所有状态进行存储,直接以一个变量来维护即可。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size()<=1) return 0;
        int min_v = prices[0];//维护一个股票的最小值
        int res = 0;
        for(int i=0;i<prices.size();i++){
            res = max(res, prices[i]-min_v);
            min_v = min(min_v, prices[i]);
        }
        return res;
    }
};

题目II:买卖多次

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

分析

关键点在于同一支股票可以买卖多次。

本题可以直接使用贪心。[7, 1, 5, 6] 第二天买入,第四天卖出,收益最大(6-1),所以怎么判断不是第三天就卖出了呢? 这里就把问题复杂化了,根据题目的意思,当天卖出以后,当天还可以买入,所以其实可以第三天卖出,第三天买入,第四天又卖出((5-1)+ (6-5) === 6 - 1)。所以算法可以直接简化为只要今天比昨天大,就卖出。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        //使用贪心思想
        int res = 0;
        for(int i=1;i<prices.size();i++){
            if(prices[i]>prices[i-1]) res += prices[i]-prices[i-1];
        }
        return res;    
    }
};

题目III:买卖两次

分析

本题可以是第一题的改进,在第一题的基础上进行扫描两次。设状态f(i),表示区间 [ 0 , i ] [0, i] [0i] ( 0 < = i < = n − 1 ) (0<=i<=n-1) (0<=i<=n1)的最大利润,状态g(i),表示区间区间 [ i , n − 1 ] [i, n-1] [in1] ( 0 < = i < = n − 1 ) (0<=i<=n-1) (0<=i<=n1)的最大利润,则最终答案为max (f(i) + g(i)) , ( 0 < = i < = n − 1 ) (0<=i<=n-1) (0<=i<=n1)

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size()<=1) return 0;
        int min_v = prices[0];//维护一个股票的最小值
        int res = 0;
        vector<int> f(prices.size(), 0);
        vector<int> g(prices.size(), 0);
        for(int i=0;i<prices.size();i++){
            res = max(res, prices[i]-min_v);
            f[i] = res;
            min_v = min(min_v, prices[i]);
        }
        res = 0;
        int max_v = prices[prices.size()-1]; //从右往左的话,则维护一个股票的最大值
        for(int i=prices.size()-2;i>=0;i--){
            res = max(res, max_v - prices[i]);
            g[i] = res;
            max_v = max(max_v, prices[i]);
        }

        int ans = 0;
        for(int i=0;i<prices.size();i++) ans = max(ans, f[i]+g[i]);
        return ans;
    }
};

题目IV:至多买卖k次

分析

本题是对上情况的泛化。主要采用三维dp, 定义状态dp[i][k][j]其中j可以取0和1。

  • dp[i][k][1]表示前i天,最多交易k次,且当前持有股票时所获得的最大利润
  • dp[i][k][0]表示前i天,最多交易k次,且当前不持有股票时所获得的最大利润
  • 注意一次交易指的是买和卖这两个合起的动作
class Solution {
public:
    int maxProfit(int max_k, vector<int>& prices) {

        int n = prices.size();
        //当k大于n/时就表示交易的次数无数次
        if(max_k>n/2) return maxMultiProfit(prices);
        //主要采用三维dp, 定义状态dp[i][k][j]其中j可以取0和1
        //dp[i][k][1]表示前i天,最多交易k次,且当前持有股票时所获得的最大利润
        //dp[i][k][0]表示前i天,最多交易k次,且当前不持有股票时所获得的最大利润
        //注意一次交易指的是买和卖这两个合起的动作
        vector<vector<vector<int>>> dp(n, vector<vector<int>>(max_k+1, vector<int>(2, 0)));
        for(int i=0;i<prices.size();i++){
            //处理base case  
            for(int k=max_k;k>=1;k--){
                if((i-1)==-1){//即i=0
                //当第一维为-1的时候
                    dp[i][k][0] = 0;
                    dp[i][k][1] = -prices[i];
                }
                else{
                    dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1]+prices[i]);//dp[i-1][k][0]表示i的上一天也没有股票也没有交易,即休息,dp[i-1][k-1][1]表示i的上一天有股票, 并交易卖了股票
                    dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0]-prices[i]);//第一个是不交易,第二个是买入了股票
                }
            } 
        }
        return dp[n-1][max_k][0];
    }
    //可以多次买卖同一支股票
    int maxMultiProfit(vector<int>& prices) {
        //使用贪心思想
        int res = 0;
        for(int i=1;i<prices.size();i++){
            if(prices[i]>prices[i-1]) res += prices[i]-prices[i-1];
        }
        return res; 
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值