LeetCode Week12: Best Time to Buy and Sell Stock系列

本周完成的习题依然是Dynamic Programming系列的题目,这里选择Best Time to Buy and Sell Stock系列的五道题目进行分析。

一、Best Time to Buy and Sell Stock I

题目

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

If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.

Example 1:

Input: [7, 1, 5, 3, 6, 4]
Output: 5

max. difference = 6-1 = 5 (not 7-1 = 6, as selling price needs to be larger than buying price)

Example 2:

Input: [7, 6, 4, 3, 1]
Output: 0

In this case, no transaction is done, i.e. max profit = 0.

我的分析

这道题是在给定每一天的股票的金额的情况下,需要求的是在某一天买入买入一支股票后,在这个时间之后的某一天再把这支股票卖出时,如何有最大的收益?最简单的当然是,我们在找到两个时间段 i j ,其中 i < j 。在第 i 天股票的金额最低,那么可以在这一天买入股票,而在第 j 天股票金额最高,那么可以在这一天把股票卖出,这样的股票的收益就会最大

代码

通过上述分析,可以得到如下所示的代码:

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

即:不停的寻找在最小的股票金额下买入股票后在当前卖出的最大收益。

二、Best Time to Buy and Sell Stock II

题目

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 as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

我的分析

同样是买入卖出股票,但是这一题的要求是可以进行任意次的股票买入卖出,而且一定是要卖出股票后才能再购买股票,那么就可以变成对于任意连续的两天 i j i+1=j ),客户可以在第 i 天买入股票第 j 天卖出,同时可以第 j 天买入股票第 j+1 天卖出。这样就可以使得第二天股票金额比第一天的股票金额高时卖出股票得到收益,这是非常明显的贪心算法。

代码

通过上述分析,可以得到如下所示的代码:

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

三、Best Time to Buy and Sell Stock III

题目

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).

我的分析

这道题跟II相比,多了一个限制:只能执行两次买入卖出的操作,那么利用动态规划的方法可以求解。
假设在第i天时,在其前面的时间进行了一次买入卖出操作,收益为PreProfit,在其后面的时间也进行了一次买入卖出操作,收益为PostProfit,那么对于第i天,可以得到的最大的收益是PreProfit+PostProfit,最终遍历每一天的PreProfit+Postfit,最大值即位最终答案。

对于在第i天的前面的买入卖出的最大收益的计算,可以直接参考Best Time to Buy and Sell Stock I的操作,即不停的的寻找在第i天以前在最小的股票金额下买入股票后在有最大股票金额时卖出的最大收益。

对于在第i天的后面的买入卖出的最大收益的计算,可以进行反向的思考,即从最后一个元素倒序遍历,找到遍历过的天数里面股票可以有的最大金额,然后判断在第i天可以买入的话可以卖出的最大收益,与在这一天之后买入卖出的收益哪一个更大,将二者之中的最大值看作这一天买入股票后能得到的最大收益。

代码

通过上述分析,可以得到如下所示的代码:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.empty()) return 0;
        int len = prices.size();
        vector<int> PreProfit(len,0), PostProfit(len,0);
        int minPri = prices[0]; 
        for(int i = 1; i < len; i++){
            PreProfit[i] = max(PreProfit[i-1],max(prices[i]-minPri,0));
            minPri = min(minPri,prices[i]);
        }
        int maxPri = prices[len-1];
        for(int j = len-2; j >= 0; j--){
            PostProfit[j] = max(PostProfit[j+1],max(maxPri-prices[j],0));
            maxPri = max(maxPri,prices[j]);
        }
        int maxPro = 0;
        for(int i = 0; i < len; i++)
            maxPro = max(PreProfit[i]+PostProfit[i],maxPro);
        return maxPro;
    }

};

四、Best Time to Buy and Sell Stock IV

题目

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 k transactions.

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

我的分析

这一道题其实是III的普遍版,也就是说,III中的情况其实是k=2的特殊情况,现在我们能进行k次股票的买入卖出操作。我们在处理题目III时使用了两个数组,分别表示在第 i 天时前面有过一次交易的最大收益和后面有过一次交易的最大收益,但是现在,我们在第i天时前面可以进行的最多有z次,后面最多是k-z次,所以不能再用上述的方法求解。
这里参考Code_Ganker的做法,可以利用两种收益数组:local和global来解决。

  • local[i][j]:local[i][j]表示的是第i天进行j次交易的局部最好收益,局部体现在这最后一次交易是在第i天进行的。对于局部收益,存在两种情况:

    1. 在第i-1天以前完成了j-1次交易,在第i天完成了第j次交易,那么 local[i][j]=global[i1][j1]+abs(diff) ,diff是第j次交易的收益;
    2. 在第i-1天就完成了j次交易,那么为了保证最后一次交易是在第i天进行的,就需要把i-1天完成的第j次交易变成在第i天来交易,即 local[i][j]=local[i1][j]+diff .
      所以 local[i][j]=max(global[i1][j1]+abs(diff),local[i1][j]+diff) .
  • global[i][j]:global[i][j]表示的是第i天进行j次交易的全局最好收益,显然 global[i][j]=max(global[i][j],local[i][j])
    因为global和local其实只需要记录第j次的最大收益(因为对于某一天,如果它执行的j次交易比其他天执行的要高,把么我存储这个更大的值就好了),所以可以把两个变量变做一维数组处理,最终返回global[k]即可。

代码

通过上述分析,可以得到如下所示的代码:

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        int len = prices.size();
        // local: 当前的最大局部收益,并且在当天会卖出股票
        // global: 当前的全局最大收益,当天股票是否卖出不固定
        if(k >= len){
            int res = 0;
            for(int i = 1; i < prices.size(); i++)
                res += max(prices[i]-prices[i-1],0);
            return res;
        }
        vector<int>local(k+1,0), global(k+1,0);
        for(int i = 1; i < len; i++){
            int diff = prices[i]-prices[i-1];
            for(int j = k; j >= 1; j--){
                local[j] = max(global[j-1]+max(diff,0),local[j]+diff);
                global[j] = max(local[j],global[j]);
            }
        }
        return global[k];
    }
};

这里还考虑了k远远大于股票金额序列的情况,此时股票的买入卖出情况与II一致。
如果令k=2,那么能得到与III中的代码得到相同的结果。

你可能感兴趣的:(LeetCode)