代码随想录Day52

今天继续学习动规解决相关问题。

121.买卖股票的最佳时机

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

思路:

1.本题有一点类似求连续子区间最大和,一开始想到的是直接通过贪心算法找到局部最优,即找到左边的最小和右边的最大,然后两者之差就能得到最大的金额。在观看题解后了解了本题的动规做法。

2.首先想dp数组的含义,由于本题涉及到买卖股票,可能一开始会想到本题对于第i天股票的情况有两种,即买或者卖。但如果这样划分情况的话,在卖出股票之后的那些天又该用什么方式来表示呢?显然这样划分是还要额外添加状态导致整个过程变复杂的,因此在以上分析之上我们换一种划分情况的思路,将第i天有没有持有股票作为划分的依据,这样就能涵盖所有情况了。

因此dp数组含义为dp[i][0]表示第i天持有股票的最大金额,dp[i][1]表示第i天不持有股票的最大金额。

3.然后想递推公式,首先对于dp[i][0],第i天持有股票有两种情况:(1)第i天买入,此时的金额为-prices[i];(2)第i-1天就已经持有股票,那么就维持现状等于dp[i - 1][0]。

对于dp[i][1],第i天不持有股票有两种情况:(1)第i天卖出,此时的金额为dp[i - 1][0] + prices[i];(2)第i - 1天就已经不持有股票,那么就维持现状等于dp[i - 1][1]。

因为是求最大金额,以上都需要取最大值。

4.然后想初始化,由递推公式可以看出所有的后续状态都是由dp[0][0],dp[0][1]推导出来的,因此这两位必须初始化,dp[0][0]只能代表第0天就购入股票,那么就初始化为-prices[0],dp[0][1]表示第0天不持有股票,因此就是0.

5.然后想遍历顺序,很明显本题是需要从前往后遍历的。

class Solution {
public:
    int maxProfit(vector& prices) {
        vector> dp(prices.size(), vector(2));
        //dp[i][0]表示持有股票的最大现金,dp[i][1]表示不持有股票的最大现金
        dp[0][0] = -prices[0];
        dp[0][1] = 0;

        for(int i = 1; i < prices.size(); i++){
            dp[i][0] = max(-prices[i], dp[i - 1][0]);
            dp[i][1] = max(dp[i - 1][0] + prices[i], dp[i - 1][1]);
        }

        return dp[prices.size() - 1][1];
    }
};

启发:

1.本题对于dp数组的含义确定很有讲究,有的时候不恰当的dp数组含义会导致无法覆盖多种情况使得解题变得极其困难,因此需要适时地换个角度想想dp数组的含义。本题的“持有”与“不持有”并不代表“买入”和“卖出”,后者只是对应上前者的一种情况

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

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

返回 你能获得的 最大 利润 。

示例 1:

输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
     总利润为 4 + 3 = 7 。

思路:

1.点开这道题才发现原来上一道题的莫名熟悉感来自这道题,这道题才是当初在贪心算法中讲解的原题,通过将最大利润拆分成每一天的利润,只收集利润为正数的部分作为最终的利润即可。

2.本题如果采用动规的方法来做的话,其实基本和上一题一致,唯一不同的一点在于因为现在可以买卖多次股票,因此dp[i][0]所对应的情况如果是当天买入股票的话,应该等于dp[i - 1][1] - prices[i](上一题中只能买卖一次股票,因此当天买入股票的话金额一定是0 - prices[i])。

贪心:

class Solution {
public:
    int maxProfit(vector& prices) {
        int result = 0;
        int count = 0;
        for(int i = 1; i < prices.size(); i++){
            count = prices[i] - prices[i - 1];
            //只保证取正数利润加入最终利润
            result += max(count, 0);
        }

        return result;
    }
};

动规:

class Solution {
public:
    int maxProfit(vector& prices) {
        vector> dp(prices.size(), vector(2));

        dp[0][0] = -prices[0];
        dp[0][1] = 0;

        for(int i = 1; i < prices.size(); i++){
            dp[i][0] = max(dp[i - 1][1] - prices[i], dp[i - 1][0]);
            dp[i][1] = max(dp[i - 1][0] + prices[i], dp[i - 1][1]);
        }

        return dp[prices.size() - 1][1];
    }
};

启发:

1.对于买卖股票这一系列题目,尽管贪心算法能够解决,但贪心算法并没有一个实际的模板,每一题都要针对不同的情景进行调整,因此这一系列题目动规才是最主要的通解。但有些时候贪心的思路反而会比动规更加简单。

你可能感兴趣的:(代码随想录,算法,动态规划,leetcode,数据结构,c++)