123. Best Time to Buy and Sell Stock III

本文详细解析了在给定股票价格数组的情况下,如何通过至多两次买卖操作获得最大利润的算法设计思路。文章通过动态规划的方法,分别从前向后及从后向前遍历数组来计算可能的最大收益。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most two transactions.

Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

分析:这一题约束最多只能买卖两次股票,并且手上最多也只能持有一支股票。因为不能连续买入两次股票,所以买卖两次肯定分布在前后两个不同的区间。设p(i) = 区间[0,1,2...i]的最大利润 + 区间[i,i+1,....n-1]的最大利润(式子中两个区间内分别只能有一次买卖,这就是第一道题的问题),那么本题的最大利润 = max{p[0],p[1],p[2],...,p[n-1]}。根据第一题的算法2,我们可以求区间[0,1,2...i]的最大利润;同理可以从后往前扫描数组求区间[i,i+1,....n-1]的最大利润,其递归式如下:

dp[i-1] = max{dp[i], maxprices - prices[i-1]}  ,maxprices是区间[i,i+1,...,n-1]内的最高价格。                                                          

因此两趟扫描数组就可以解决这个问题,代码如下:

class Solution {
public:
    int maxProfit(vector<int> &prices) {
        // IMPORTANT: Please reset any member data you declared, as
        // the same Solution instance will be reused for each test case.
        const int len = prices.size();
        if(len <= 1)return 0;
        int maxFromHead[len];
        maxFromHead[0] = 0;
        int minprice = prices[0], maxprofit = 0;
        for(int i = 1; i < len; i++)
        {
            minprice = min(prices[i-1], minprice);
            if(maxprofit < prices[i] - minprice)
                maxprofit = prices[i] - minprice;
            maxFromHead[i] = maxprofit;
        }
        int maxprice = prices[len - 1];
        int res = maxFromHead[len-1];
        maxprofit = 0;
        for(int i = len-2; i >=0; i--)
        {
            maxprice = max(maxprice, prices[i+1]);
            if(maxprofit < maxprice - prices[i])
                maxprofit = maxprice - prices[i];
            if(res < maxFromHead[i] + maxprofit)
                res = maxFromHead[i] + maxprofit;
        }
        return res;
    }
};

9.15号更新

------------------

III是这三题中最难的。允许两次买卖,但同一时间只允许持有一支股票。也就意味着这两次买卖在时间跨度上不能有重叠(当然第一次的卖出时间和第二次的买入时间可以是同一天)。既然不能有重叠可以将整个序列以任意坐标i为分割点,分割成两部分:

prices[0:n-1] => prices[0:i] + prices[i:n-1]

对于这个特定分割来说,最大收益为两段的最大收益之和。每一段的最大收益当然可以用I的解法来做。而III的解一定是对所有0<=i<=n-1的分割的最大收益中取一个最大值。为了增加计算效率,考虑采用dp来做bookkeeping。目标是对每个坐标i:

1. 计算A[0:i]的收益最大值:用minPrice记录i左边的最低价格,用maxLeftProfit记录左侧最大收益
2. 计算A[i:n-1]的收益最大值:用maxPrices记录i右边的最高价格,用maxRightProfit记录右侧最大收益。
3. 最后这两个收益之和便是以i为分割的最大收益。将序列从左向右扫一遍可以获取1,从右向左扫一遍可以获取2。相加后取最大值即为答案。


注意:算法需要两次扫描,一次从前向后扫,枚举的是是否在当前元素抛出,一次是从后向前扫,枚举的是是否在当前元素买入!!!

class Solution {
public:
    int maxProfit(vector<int> &prices) {
        if(prices.empty()) return 0;
        int n = prices.size();
        vector<int> leftProfit(n,0);
        
        int maxLeftProfit = 0, minPrice = prices[0];
        for(int i=1; i<n; i++) {
            if(prices[i]<minPrice)
                minPrice = prices[i];
            else
                maxLeftProfit = max(maxLeftProfit, prices[i]-minPrice);
            leftProfit[i] = maxLeftProfit;
        }
        
        int ret = leftProfit[n-1]; //注意对ret的初始化
        int maxRightProfit = 0, maxPrice = prices[n-1];
        for(int i=n-2; i>=0; i--) {
            if(prices[i]>maxPrice)
                maxPrice = prices[i];
            else
                maxRightProfit = max(maxRightProfit, maxPrice-prices[i]);
            ret = max(ret, maxRightProfit + leftProfit[i]);
        }
        
        return ret;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值