LeetCode简易题解--121、122、123、188

121. Best Time to Buy and Sell Stock

目标是找到max(prices[j]−prices[i]), for every i and j such that j > i.
直接用两层循环可能会超时。
更好的方法是遍历一遍prices[]数组,每一次遍历执行一下两个操作:

  • 记录下目前为止遇到的最小值
  • 计算当前price与最小值的差值,与已经得到的前面部分的最大值作比较,取较大者。

代码如下:

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

122. Best Time to Buy and Sell Stock II

与上一道题同样,是一道简单题,但是直接用两层循环可能会超时。
可以对prices[]进行遍历,找到每一段升序子段,结果加上首尾差值即可。
代码如下:

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

更简洁的方法是:对每一个prices[i],如果prices[i] - prices[i-1] > 0,则结果加上该差值。只要后一个数比前一个数大,就加上两数的差值,效果与以上方法是一样的。

123. Best Time to Buy and Sell Stock III

初始收益profit = 0,购买时profit -= prices[i],出售时profit += prices[i]
这里有四个变量:

  • buy1:表示只买了一块石头的收益
  • sell1:表示买一块并卖了一块石头的收益
  • buy2:表示买了第二块石头时的收益,根据题目,此时肯定已经买卖过一次石头了
  • sell2:表示卖了第二块石头时的收益

prices[]进行遍历,每一次遍历都计算出以上4个值,则最后的结果必定是max(sell1, sell2)

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.size() <= 1) return 0;
        int buy1 = INT_MIN, buy2 = INT_MIN;
        int sell1 = 0, sell2 = 0;
        for (int i = 0; i < prices.size(); ++i) {
            sell2 = max(sell2, buy2 + prices[i]);
            buy2 = max(buy2, sell1 - prices[i]);
            sell1 = max(sell1, buy1 + prices[i]);
            buy1 = max(buy1, -prices[i]);
        }
        return sell2 < 0 ? 0 : sell2;
    }
};

以上代码的一些解释:
Q: 为何计算顺序是sell2buy2sell1buy1
A: 因为每一个值都是根据上一步计算的结果推出来的。以sell2为例,它是根据上一步的buy2推导出来的,若是buy2先于sell2计算,则在计算sell2时,所使用的buy2的值是这一步的结果,而不是上一步的结果。其他变量类似。

Q: 为什么直接返回sell2
A: sell2是根据buy2计算的,buy2是根据sell1计算的,这会导致sell2始终比sell1大。

PS:以上思路来自LeetCode的Disscussion。

188. Best Time to Buy and Sell Stock IV

用动态规划的思路来写。
将买卖一次石头称作一次事务,只有执行至少一次事务时才会有收益。
dp[i][j]表示:prices[]从 0 到 j-1 个元素并且执行了 i 个事务时的最大收益。
首先确定边界:

dp[i][0] = 0 : 第一个元素的地方收益当然为0
dp[0][j] = 0 : 一次事务都没执行,收益当然为0

现在考虑一般情况:
dp[i][j]表示到prices[j-1]并且执行了j次事务时的最大收益,该值来源于两个方面:

  • dp[i][j-1] :到prices[j-1](即第j天)时,事务数量没有增加(没有卖出石头,收益也就不会增加)
  • max( prices[j] - prices[jj] + dp[i-1, jj] {0<=jj<j} ) : 到prices[j-1](即第j天)时,事务数量增加1

以上第二个值的解释:prices[jj]之前(0到jj-1)视为一个执行了i-1次事务的子段,dp[i-1][jj]为此时的最大收益,该值加上jj+1到j之间的最大差值,就可能是dp[i][j]。但是这个jj可能会有多个值,因此这些prices[j] - prices[jj] + dp[i-1, jj] {0<=jj<j}中的最大值即是dp[i][j]的两个可能来源之一。

dp[i][j]就是以上两个值之中的最大值:

p[i][j] = max(dp[i][j-1], max( prices[j] - prices[jj] + dp[i-1, jj]))
        = max(dp[i, j-1], prices[j] + max(dp[i-1, jj] - prices[jj]));

另外,记n为数组长度,则最多有n/2个事务(每个事务至少涉及两个元素),
因此当k>n/2时,该题跟122题是一样的。

代码如下:

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        if (prices.size() <= 1) return 0;

        int n = prices.size();
        if (k > n / 2) return quick_solve(prices);

        vector<vector<int> > dp(k + 1, vector<int>(n, 0));
        for (int i = 1; i <= k; ++i) {
            int tmp_max = -prices[0];
            for (int j = 1; j < n; ++j) {
                dp[i][j] = max(dp[i][j - 1], tmp_max + prices[j]);
                tmp_max = max(tmp_max, dp[i - 1][j] - prices[j]);
            }
        }
        return dp[k][n - 1];
    }

    int quick_solve(vector<int>& prices) {
        int n = prices.size();
        int res = 0;
        for (int i = 1; i < n; ++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、付费专栏及课程。

余额充值