ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LeetCode上的各种股票最大收益

2022-02-05 20:30:39  阅读:220  来源: 互联网

标签:int 股票 收益 lens vector max prices LeetCode dp


LeetCode上的各种股票最大收益

对于力扣平台上的股票类型的题目:

  • 121 买卖股票的最佳时机

  • 122 买卖股票的最佳时机 II

  • 123 买卖股票的最佳时机 III

  • 124 买卖股票的最佳时机 IV

  • 309 最佳买卖股票时机含冷冻期

  • 714 买卖股票的最佳时机含手续费

  • 剑指 Offer 63. 股票的最大利润

关键是要仔细分析题意,将每天结束后可能存在的状态列出来,并考虑当天与前一天的可能存在的状态转移的关系。另外要注意初始化空间复杂度的优化

121. 买卖股票的最佳时机

dp数组含义

本题在某一天共可能有两种状态:

  • 持有股票
  • 不持有股票

我们构造的 dp 数组的规模为 2 × n 2\times n 2×n,在 C++ 中,即为:vector<<vevtor<int>> dp(prices.size(), vector<int> (2, 0));

  • d p [ i ] [ 0 ] dp[i][0] dp[i][0] 表示在第 i i i 天结束后,在持有股票的状态下的最大收益
  • d p [ i ] [ 1 ] dp[i][1] dp[i][1] 表示在第 i i i 天结束后,在不持有股票的状态下的最大收益

递推公式

  • 对于第 i i i 天持有股票时的收益,即 d p [ i ] [ 0 ] dp[i][0] dp[i][0],可能由以下情况推导而来:

    • 第 i − 1 i-1 i−1 天是也是持有股票的,即当日未进行任何操作: d p [ i − 1 ] [ 0 ] dp[i-1][0] dp[i−1][0]

    • 第 i − 1 i-1 i−1 天是未持有股票的,即当日购买了股票,由于我们只能进行一次股票买卖操作,因此在购买股票之前的 d p [ i − 1 ] [ 1 ] dp[i-1][1] dp[i−1][1] 一定是 0,因此应当减去当日股票价格: 0 − p r i c e s [ i ] 0-prices[i] 0−prices[i]

    应当取最大值,从而 d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , − p r i c e s [ i ] ) dp[i][0]=max(dp[i-1][0],-prices[i]) dp[i][0]=max(dp[i−1][0],−prices[i])

  • 对于第 i i i 天未持有股票时的收益,即 d p [ i ] [ 1 ] dp[i][1] dp[i][1],可能由以下情况推导而来:

    • 第 i − 1 i-1 i−1 天是是持有股票的,即当日将股票卖出,应当 d p [ i − 1 ] [ 0 ] dp[i-1][0] dp[i−1][0] 加上当日股票价格: d p [ i − 1 ] [ 0 ] + p r i c e s [ i ] dp[i-1][0]+prices[i] dp[i−1][0]+prices[i]
    • 第 i − 1 i-1 i−1 天是未持有股票的,即当日无操作: p r i c e s [ i − 1 ] [ 1 ] prices[i-1][1] prices[i−1][1]

    应当取最大值,从而 d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] + p r i c e s [ i ] ) dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i]) dp[i][1]=max(dp[i−1][1],dp[i−1][0]+prices[i])

初始化

dp 数组中所有项需在 d p [ 0 ] [ 0 ] dp[0][0] dp[0][0]、 d p [ 0 ] [ 1 ] dp[0][1] dp[0][1] 确定的情况下得到。

  • d p [ 0 ] [ 0 ] dp[0][0] dp[0][0] 表示第一天持有股票,即第一天购入,应为 − p r i c e s [ 0 ] -prices[0] −prices[0]
  • d p [ 0 ] [ 1 ] dp[0][1] dp[0][1] 表示第一天未持有股票,应为 0

遍历顺序

从前向后

代码

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

空间优化

很明显,第 i i i 天的情况只依赖于前一天的情况,而与再前面的情况没有关系,因此,我们没有必要维护一整个 dp 数组,而是维护常数变量即可,优化后:

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

另一种做法

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int ans = 0, prevMin = INT_MAX;
        for (int n: prices) {
            ans = max(ans, n - prevMin);
            prevMin = min(prevMin, n);
        }
        return ans;
    }
};

122. 买卖股票的最佳时机 II

本题与121 题的唯一区别在于可以多次买卖股票,但要注意同时只能持有一只股票

dp数组含义

  • d p [ i ] [ 0 ] dp[i][0] dp[i][0] 表示在第 i i i 天结束后,持有股票时的最大收益
  • d p [ i ] [ 1 ] dp[i][1] dp[i][1] 表示在第 i i i 天结束后,不持有股票时的最大收益

递推公式

注意由于这里与上一题的唯一区别在于允许多次股票买卖,因此递推公式与上面的四种情况基本相同,唯一一点在于由于我们可以进行多次买卖,所以在某次购买股票之前的未持有股票的收益不一定是 0 了(我们可能在这次操作之前已经通过几次操作收获了一些利润)。

即对于 d p [ i ] [ 0 ] dp[i][0] dp[i][0] 在前一日不持有股票的情况从 0 − p r i c e s [ i ] 0-prices[i] 0−prices[i] 变为了 d p [ i − 1 ] [ 1 ] − p r i c e s [ i ] dp[i-1][1]-prices[i] dp[i−1][1]−prices[i],从而 d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] − p r i c e s [ i ] ) dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i]) dp[i][0]=max(dp[i−1][0],dp[i−1][1]−prices[i])

初始化遍历顺序与前题类似。

代码

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

注意代码中的唯一区别在于:

dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i]);

原因上面已经解释过。

空间优化的实现与上面也类似,这里就不贴了。

另一种写法

本题有另一种做法,由于我们可以进行无限次交易且当日即可以买入又可以卖出,所以考虑:

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

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

123. 买卖股票的最佳时机 III

本题相较于前面两题要复杂不少,关键是最多两次交易,即有可能可以是 0,1,2 次交易,我们需要列举处理的情况多了不少。

dp数组的定义

本题中,我们一共要考虑每一天的五种状态:

  • 尚未进行操作
  • 第一次买入后
  • 第一次卖出后
  • 第二次买入后
  • 第二次卖出后

注意我们这里要强调各个状态是 买入/卖出 后 ,即不一定是当日买入/卖出,可能是 i − 1 i-1 i−1, i − 2 i-2 i−2 …日进行的操作,都归为该状态。并且,注意到,这些状态是有序的,即一定是一次经历到的。

由此,我们 dp 数组的含义为: d p [ i ] [ j ] dp[i][j] dp[i][j] 表示,第 i i i 天处于状态 j j j 时的最大收益。

递推公式

  • 对于第 i i i 日结束后。处于第一次买入后状态,即 d p [ i ] [ 1 ] dp[i][1] dp[i][1],可能由以下情况推导而来:

    • 前面已经买入,当日未进行操作: d p [ i − 1 ] [ 1 ] dp[i-1][1] dp[i−1][1]
    • 当日买入: d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] dp[i-1][0]-prices[i] dp[i−1][0]−prices[i]

    取最大值,则有: d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] ) dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]) dp[i][1]=max(dp[i−1][1],dp[i−1][0]−prices[i])

  • 对于第 i i i 日处于第一次卖出后,即 d p [ i ] [ 2 ] dp[i][2] dp[i][2],可能由以下情况推导而来:

    • 前面已经卖出,当日未进行任何操作: d p [ i − 1 ] [ 2 ] dp[i-1][2] dp[i−1][2]
    • 当日卖出: d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] dp[i-1][1]+prices[i] dp[i−1][1]+prices[i]

    则: d p [ i ] [ 2 ] = m a x ( d p [ i − 1 ] [ 2 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] ) dp[i][2]=max(dp[i-1][2],dp[i-1][1]+prices[i]) dp[i][2]=max(dp[i−1][2],dp[i−1][1]+prices[i])

  • 同理, d p [ i ] [ 3 ] = m a x ( d p [ i − 1 ] [ 3 ] , d p [ i − 1 ] [ 2 ] − p r i c e s [ i ] ) dp[i][3]=max(dp[i-1][3],dp[i-1][2]-prices[i]) dp[i][3]=max(dp[i−1][3],dp[i−1][2]−prices[i])

  • 同理, d p [ i ] [ 4 ] = m a x ( d p [ i − 1 ] [ 4 ] , d p [ i − 1 ] [ 3 ] + p r i c e s [ i ] ) dp[i][4]=max(dp[i-1][4],dp[i-1][3]+prices[i]) dp[i][4]=max(dp[i−1][4],dp[i−1][3]+prices[i])

代码

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int lens = prices.size();
        vector<vector<int>> dp(lens, vector<int> (5));
        dp[0][1] = -prices[0], dp[0][3] = -prices[0];
        for (int i=1; i<lens; ++i) {
            dp[i][0] = dp[i-1][0];
            dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]);
            dp[i][2] = max(dp[i-1][2], dp[i-1][1] + prices[i]);
            dp[i][3] = max(dp[i-1][3], dp[i-1][2] - prices[i]);
            dp[i][4] = max(dp[i-1][4], dp[i-1][3] + prices[i]);
        }
        return dp[lens - 1][4];
    }
};

优化代码

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int fstBuy = INT_MIN, fstSell = 0;
        int secBuy = INT_MIN, secSell = 0;
        for (int p: prices) {
            fstBuy = max(fstBuy, -p);
            fstSell = max(fstSell, fstBuy + p);
            secBuy = max(secBuy, fstSell - p);
            secSell = max(secSell, secBuy + p);
        }
        return secSell;
    }
};

188. 买卖股票的最佳时机 IV

上一题的一般化,分好奇偶即可:

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        if (prices.empty()) return 0;
        int lens = prices.size();
        int wide = 2 * k + 1;
        vector<vector<int>> dp(lens, vector<int> (wide, 0));
        for (int j=0; j<wide; ++j) if (j % 2 == 1) dp[0][j] = -prices[0];
        for (int i=1; i<lens; ++i) {
            dp[i][0] = dp[i-1][0];
            for (int j=1; j<wide; ++j) {
                if (j % 2 == 1) dp[i][j] = max(dp[i-1][j], dp[i-1][j-1] - prices[i]);
                else if (j % 2 == 0) dp[i][j] = max(dp[i-1][j], dp[i-1][j-1] + prices[i]);
            }
        }
        return dp[lens - 1][wide - 1];
    }
};

309. 最佳买卖股票时机含冷冻期

dp数组含义

本题中每天可能的状态有三种:

  • 持有股票
  • 不持有股票,今天卖出,后一天为冷冻期
  • 不持有股票,非今天卖出,后一天不为冷冻期

d p [ i ] [ j ] dp[i][j] dp[i][j] 表示第 i i i 天结束后,在第 j j j 中状态下的最大收益

递推公式

  • 当日结束后为持有股票状态时,即 d p [ i ] [ 0 ] dp[i][0] dp[i][0] ,可能由以下情况推导而来:

    • 当日买入股票,今天不能是冷冻期, d p [ i − 1 ] [ 2 ] − p r i c e s [ i ] dp[i-1][2]-prices[i] dp[i−1][2]−prices[i]
    • 之前就已经买入了股票,当日未进行操作: d p [ i − 1 ] [ 0 ] dp[i-1][0] dp[i−1][0]

    则, d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] − p r i c e s [ i ] , d p [ i − 1 ] [ 2 ] − p r i c e s [ i ] ) dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i],dp[i-1][2]-prices[i]) dp[i][0]=max(dp[i−1][0],dp[i−1][1]−prices[i],dp[i−1][2]−prices[i])

  • 当日卖出,即 d p [ i ] [ 1 ] dp[i][1] dp[i][1]

    • 今天卖出,前一天必为持股状态, d p [ i − 1 ] [ 0 ] + p r i c e s [ i ] dp[i-1][0]+prices[i] dp[i−1][0]+prices[i]

    则, d p [ i ] [ 1 ] = d p [ i − 1 ] [ 0 ] + p r i c e s [ i ] dp[i][1]=dp[i-1][0]+prices[i] dp[i][1]=dp[i−1][0]+prices[i]

  • 非当日卖出,即 d p [ i ] [ 2 ] dp[i][2] dp[i][2],可能由以下情况推导而来:

    • 前一日卖出,今日冷冻期, d p [ i − 1 ] [ 1 ] dp[i-1][1] dp[i−1][1]
    • 非前一日卖出,今日非冷冻期: d p [ i − 1 ] [ 2 ] dp[i-1][2] dp[i−1][2]

    则 d p [ i ] [ 2 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 2 ] ) dp[i][2]=max(dp[i-1][1],dp[i-1][2]) dp[i][2]=max(dp[i−1][1],dp[i−1][2])

代码

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

714. 买卖股票的最佳时机含手续费

就加个手续费,和 122 区别不大。

dp数组含义

本题中每一天结束后有两种状态:持有股票和不持有股票

  • d p [ i ] [ 0 ] dp[i][0] dp[i][0] 表示第 i i i 天结束后处于持股状态时的最大收益
  • d p [ i ] [ 1 ] dp[i][1] dp[i][1] 表示第 i i i 天结束后处于未持股状态时的最大收益

递推公式

  • 对于第 i i i 天结束后,处于持股状态,即 d p [ i ] [ 0 ] dp[i][0] dp[i][0],可能由两种状态推导得到:

    • 前面买入,当日未进行操作: d p [ i − 1 ] [ 0 ] dp[i-1][0] dp[i−1][0]
    • 当日买入,并支付手续费 d p [ i − 1 ] [ 1 ] − p r i c e s [ i ] − f e e dp[i-1][1]-prices[i]-fee dp[i−1][1]−prices[i]−fee

    则: d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] − p r i c e s [ i ] − f e e ) dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i]-fee) dp[i][0]=max(dp[i−1][0],dp[i−1][1]−prices[i]−fee)

  • 对于第 i i i 天结束后,处于未持股状态,即 d p [ i ] [ 1 ] dp[i][1] dp[i][1],可能由两种状态推导得到:

    • 一直未买入或前面已卖出,当日未进行操作: d p [ i − 1 ] [ 1 ] dp[i-1][1] dp[i−1][1]
    • 当日卖: d p [ i − 1 ] [ 0 ] + p r i c e s [ i ] dp[i-1][0]+prices[i] dp[i−1][0]+prices[i]

    则: d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] + p r i c e s [ i ] ) dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i]) dp[i][1]=max(dp[i−1][1],dp[i−1][0]+prices[i])

代码

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

标签:int,股票,收益,lens,vector,max,prices,LeetCode,dp
来源: https://blog.csdn.net/weixin_44966641/article/details/122792965

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有