zoukankan      html  css  js  c++  java
  • [LeetCode]Best Time to Buy and Sell Stock

    题目:Best Time to Buy and Sell Stock

    给定一个数组,数组中一个元素表示一天的股价,求一次交易能得到的最大收益。

    思路:

    数组可能是多个升序降序组成,只要能找到一组极值,使它们的差最大就可以了。

    这样实际上就是每当找到一个极大值,就判断此时的到的差值是否比记录的最大值大,是则替换;

    而当找一个极小值,就判断它是否小于记录的极小值,是则替换。

    注意:这组极值不一定是最值,因为最大值可能在最小值的前面。

    int LeetCode::maxProfit(vector<int>& prices){
        if (!prices.size())return 0;
        int start = prices.at(0), end = 0;
        int max = 0;
        for (size_t i = 1; i < prices.size(); i++){
            if (prices.at(i) < prices.at(i - 1)){//降序
                end = prices.at(i - 1);
                if (end - start > max)max = end - start;//计算上一次升序的利益值,如果大于当前最大值,则更新最大值
                if (prices.at(i) < start)start = prices.at(i);//比较下降是的值是否小于记录的起始值,是则更新起始值
            }
        }
        if (prices.at(prices.size() - 1) - start > max)max = prices.at(prices.size() - 1) - start;
        return max;
    }

    题目:Best Time to Buy and Sell StockII

    相对于上一题,此道题没有限制交易次数,也就是说可以交易无数次,但是买之前,必须要全部卖完;

    思路:

    找到所有升序的范围,将它们的差值加起来,就能得到最大利益;

    和上面思路类似,只是在比较最大值的地方改为累加起来,同时要更新记录的极小值。

    int LeetCode::maxProfit2(vector<int>& prices){
        if (!prices.size())return 0;
        int start = prices.at(0), end = 0;
        int max = 0;
        for (size_t i = 1; i < prices.size(); i++){
            if (prices.at(i) < prices.at(i - 1)){//降序
                end = prices.at(i - 1);
                if (end - start > 0){
                    max += end - start;//计算上一次升序的利益值,如果大于当前最大值,则更新最大值
                    start = prices.at(i);
                }
                if (prices.at(i) < start)start = prices.at(i);//比较下降是的值是否小于记录的起始值,是则更新起始值
            }
        }
        if (prices.at(prices.size() - 1) - start > 0)max += prices.at(prices.size() - 1) - start;
        return max;
    }

    题目:Best Time to Buy and Sell StockIII

    这次是限制交易次数为两次,其他的相同。

    思路1:

    因为次数是固定两次,因此情况是有限的且可以确定的;因此可以分情况讨论:

    设min为所有天数中最小的价格,其对应的天数为mini;max为所有天数中最大的价格,其对应的天数为maxi。

    当mini < maxi时,已经会有一个最大收益为max - min,只需找到0-mini,maxi-size之间的最大差值和mini-maxi之间的最大反向差值(先大后小)中差值大的,加上max-min 就是最终的最大收益。

    当mini > maxi时,先找到0-maxi,mini-size之间的最大差值,在在maxi-mini之间递归按上面的两种情况求最大收益的对应最大和次大的获益值(不能把和返回)。

    数学公式化就是下面这样:(maxProfit是一个函数,下面第二个等式是表示递归,profit是前面第一道题目中的求解方法可以求出来)

    maxProfit = max - min + max{profit(0-mini),profit(mini-maxi),profit(maxi-size)}(mini < maxi)

    maxProfit = max{maxProfit(0-mini),maxProfit(mini-maxi),maxProfit(maxi-size)}(maxi < mini)

    pair<int, int> LeetCode::maxProfit3(vector<int>& prices, int i, int j){
        if (j - i < 2)return{0,0};
        int mini = i, maxi = i;
        int min = prices.at(i), max = prices.at(i);
        for (int k = i + 1; k < j; ++k){
            if (prices.at(k) > prices.at(maxi)){
                maxi = k;
            }
            else if (prices.at(k) < prices.at(mini)){
                mini = k;
            }
        }
        max = prices.at(maxi);
        min = prices.at(mini);
        if (mini < maxi){
            //sndPro第二大的最大获利值,start起始价格
            int sndPro = 0,start = prices.at(i);
            for (int k = i + 1; k <= mini; ++k){//结束点是整个数组的最小值,此时必定是下降的
                if (prices.at(k) < prices.at(k - 1)){//降序时
                    //前面升序中获益比记录的最大值还大则,更新最大值
                    if (prices.at(k - 1) - start > sndPro)sndPro = prices.at(k - 1) - start;
                    if (prices.at(k) < start)start = prices.at(k);//如果下降后的价格比记录的最小价格还小,则更新最小价格
                }
            }
            start = prices.at(mini);
            for (int k = mini + 1; k <= maxi; ++k){//最大反向差;结束点是整个数组的最大值,此时必定是上升的
                if (prices.at(k)> prices.at(k - 1)){//升序时
                    //下降的差值大于记录的第二大最大差值时,更新它
                    if (start - prices.at(k - 1) > sndPro)sndPro = start - prices.at(k - 1);
                    if (prices.at(k) > start)start = prices.at(k);//升序时的价格大于记录的最大价格时更新它
                }
            }
            start = prices.at(maxi);
            for (int k = maxi + 1; k < j; ++k){
                if (prices.at(k) < prices.at(k - 1)){//降序时
                    //前面升序中获益比记录的最大值还大则,更新最大值
                    if (prices.at(k - 1) - start > sndPro)sndPro = prices.at(k - 1) - start;
                    if (prices.at(k) < start)start = prices.at(k);//如果下降后的价格比记录的最小价格还小,则更新最小价格
                }
            }
            //可能最后是升序结束没有更新最后一次的获利
            if (prices.at(j - 1) - start > sndPro)sndPro = prices.at(j - 1) - start;
            return{ max - min, sndPro };
        }
        else{
            //first是第一大,second是第二大
            pair<int, int>p1 = maxProfit3(prices, i, maxi + 1);
            pair<int, int>p2 = maxProfit3(prices, maxi + 1, mini);
            pair<int, int>p3 = maxProfit3(prices, mini, j);
            if (p1.first < p2.first){//合并最大值和第二大值
                if (p2.second > p1.first)p1.second = p2.second;
                else p1.second = p1.first;
                p1.first = p2.first;
            }
            else if (p2.first > p1.second)p1.second = p2.first;
    
            if (p1.first < p3.first){
                if (p3.second > p1.first)p1.second = p3.second;
                else p1.second = p1.first;
                p1.first = p3.first;
            }
            else if (p3.first > p1.second)p1.second = p3.first;
            return p1;
        }
        return{ 0, 0 };
    }

    思路2:

    分类讨论情况还是很复杂的,很容易出错;而这类题目是可以使用动态规划来解决的。

    P(k,i)表示到第i天的价格时,做了k次交易时的获利。

    动态规划:P(k,i) = max{P(k,i-1),max{P(k-1,j) + pi - pj}(0 <= j <= i -1)};

            = max{P(k,i-1),pi + max{P(k-1,j) - pj}(0 <= j <= i -1)};

    可以设temp = max{P(k-1,j) - pj}(0 <= j <= i -1);

    每次更新和P(k,i)一起更新temp,使得复杂度为O(n)。

    初始条件:

    P(0,i) = 0;还没有做任何交易,没有获利

    P(k,0) = 0;第一天的获利必然为0

    int LeetCode::maxProfit3(vector<int>& prices){
        if (prices.size() < 2)return 0;
        vector<vector<int>>arr(2,vector<int>(prices.size(),0));//k为2
        int maxPro = 0;
        //temp0交易一次的temp;temp1交易2次的temp
        int temp0 = -prices.at(0),temp1 = arr.at(0).at(0) - prices.at(0);
        for (size_t i = 1; i < prices.size(); i++){
            arr.at(0).at(i) = max(arr.at(0).at(i - 1), prices.at(i) + temp0);//求P(0,i)
            temp0 = max(temp0, -prices.at(i));//更新temp0
            arr.at(1).at(i) = max(arr.at(1).at(i - 1), prices.at(i) + temp1);//求P(1,i)
            temp1 = max(temp1, arr.at(0).at(i) - prices.at(i));//更新temp1
            maxPro = max(maxPro, arr.at(1).at(i));//求最大值
        }
        return maxPro;
    }

    题目:Best Time to Buy and Sell StockIV

    这题不再是固定的两次,而是k次,其他相同;

    很显然不能再分类讨论了,那么只能使用动态规划;

    思路1:

    做法和上面的类似,只是2变成了k,空间复杂度变成了O(kn);

    int LeetCode::maxProfit4(int k, vector<int>& prices){
        int len = prices.size();
        if (len < 2) return 0;
        if (k > len / 2){//能交易次数在天数的一半以上,则表示可以随意交易,等同于maxProfit2的算法
            int ans = 0;
            for (int i = 1; i<len; ++i){
                ans += max(prices[i] - prices[i - 1], 0);//避开小于0的情况
            }
            return ans;
        }
        vector<vector<int>>arr(k + 1, vector<int>(prices.size(), 0));
        int maxPro = 0;
        vector<int> temp(k + 1, -prices.at(0));
        for (size_t i = 1; i < prices.size(); i++){
            for (int j = 1; j <= k; ++j){
                arr.at(j).at(i) = max(arr.at(j).at(i - 1), temp.at(j) + prices.at(i));//求P(k,i)
                temp.at(j) = max(temp.at(j), arr.at(j - 1).at(i) - prices.at(i));//求temp(k)
            }
            maxPro = max(maxPro, arr.at(k).at(i));//求最大值
        }
        return maxPro;
    }

    思路2:

    从上面的程序可以看出来,其实最终的最大利益值只与前一个的最大利益值和k相关,这样的话应该不需要记录所有的k个最大利益值。

    但是具体该怎么做呢?

    上面的每天都对应数组的一项,合并成n天公用一项;即是vector<vector<int>>arr(kn);=> vector<int>arr(k);将arr.at(k).at(0-n)合并为arr.at(k)

    int LeetCode::maxProfit4(vector<int>& prices, int k){
        int len = prices.size();
        if (len < 2) return 0;
        if (k > len / 2){//能交易次数在天数的一半以上,则表示可以随意交易,等同于maxProfit2的算法
            int ans = 0;
            for (int i = 1; i<len; ++i){
                ans += max(prices[i] - prices[i - 1], 0);//避开小于0的情况
            }
            return ans;
        }
        vector<int>arr(k + 1,0);//记录当前价值下的0 - k个交易后的最大收益值
        vector<int> temp(k + 1, -prices.at(0));//temp的初值
        for (size_t i = 1; i < prices.size(); i++){
            int cur = prices.at(i);
            for (int j = 1; j <= k; ++j){
                arr.at(j) = max(arr.at(j), temp.at(j) + cur);//求P(k,i)
                temp.at(j) = max(temp.at(j), arr.at(j - 1) - cur);//求temp(k)
            }
        }
        return arr.at(k);
    }
  • 相关阅读:
    数据库客户端工具Oracle SQL Developer
    Win7系统下搭建FTP
    Apache 中httpd.conf文件配置详解(转载)
    Apache启动报错Address already in use: make_sock: could not bind to...
    QTP如何准确识别Dialog中的对象
    【Apache系列】linux下Apache的常用操作
    【Apache系列】Windows下作为应用程序运行Apache
    【QTP专题】05_参数化之Excel
    CSS伪类before,after制作左右横线中间文字效果
    干货!所有常用的原型设计工具都在这里了
  • 原文地址:https://www.cnblogs.com/yeqluofwupheng/p/6777266.html
Copyright © 2011-2022 走看看