代码随想录算法训练营第四十八天|LeetCode188 买卖股票的最佳时机Ⅳ、LeetCode309 买卖股票的最佳时机含冷冻期、LeetCode714 买卖股票的最佳时机含手续费、股票问题总结

题1:

指路:188. 买卖股票的最佳时机 IV - 力扣(LeetCode)
思路与代码:

依旧定义一个二维数组dp[i][]。其中i为第i天,j为第i天的状态。类似于最多买卖四次,本题买卖k次依旧是0为不操作,1为第一次买入,2为第一次卖出,即当j为奇数时,第i天的状态为买入,当j为偶数时,第i天的状态为卖出。又因为最多有k次买卖,一次买卖两个状态,那么j小于2*k+1。关于递推公式,如果不操作则延续之前的状态,如果买入则是dp[i][1] = max(dp[i - 1][0] - prices[i], dp[i - 1][1])卖出则为dp[i][2]=max(dp[i-1][1] + prices[i], dp[i-1][2])。关于dp数组初始化,买入后手头资金为-peices[i],卖出后手头资金则为0,即dp[0][1]=-prices[i],dp[0][2]=0,dp[0][3]=-prices[0],dp[0][4]=0以此类推,就是j为奇数的时候dp[0][j]=-peices[0]。代码如下:

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
    if (prices.size() == 0) return 0;
    vector<vector<int>> dp(prices.size(), vector<int>(2 * k + 1, 0));
    // 0为持有,1为非持有
    for (int j = 1; j < 2 * k; j += 2) {
        dp[0][j] = -prices[0];
    }
    for (int i = 1; i < prices.size(); i++) {
        for (int j = 0; j < 2 * k - 1; j += 2) {
            dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
            dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
        }
    }
    return dp[prices.size() - 1][k * 2];
    }
};

题2:

指路:309. 买卖股票的最佳时机含冷冻期 - 力扣(LeetCode)
思路与代码:

这个股票问题相对之前做过的其他股票问题多了一个冷静期,即在卖出后的第一天不能买入股票,所以我们在卖出股票方面要拆分讨论,是保持着之前已经卖出的状态还是当天才卖出(卖出后的第一天即为冷静期)。那么我们定义一个数组dp[i][j],其含义依旧为第i天的状态为j,手头的现金为dp[i][j]。在这里结合dp数组和状态分析,我们将dp数组分为四个状态,dp[i][0]表示第i天持股,dp[i][1]表示保持着前面卖出股票的状态,dp[i][2]表示在第i天卖出股票,dp[i][3]表示卖出后的一天冷冻期。在这之中,dp[i][0]持股状态又分为两个状态,即保持着之前买入的状态和在第i天才买入的状态,但在当天买入中也分为两种情况,分别是在保持着卖出状态后买入和在冷冻期后买入。那么分别对其求较大值就是:

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

保持卖出股票状态中,也分为两种情况保持之前卖出的状态和冷冻期中(卖掉之后冷冻期内不可立即买入),取较大值就是dp[i][1]=max(dp[dp[i-1][1], dp[i-1][3]);在当天卖出股票的状态中只有一种情况,即dp[i][2]=dp[i-1][0]+prices[i];在冷冻期状态中依旧一种状态,即dp[i][3]=dp[i-1][2],因为冷冻期一定是卖出后的第一天股票不可操作手头资金不变。在持股状态下,资金为股票负值,即dp[0][0]=-prices[i]。因为当天的状态依附于之前的状态,那么遍历顺序是从前往后。最后讨论返回值,手头资金最大时股票一定处于卖出状态或冷冻期状态。代码如下:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
    if (prices.size() == 0) return 0;
    int n = prices.size();
    vector<vector<int>> dp(n, vector<int>(4, 0));
    //0为持股,1为保持卖出,2为当天卖出,卖出+1为冷冻期一天
    dp[0][0] = -prices[0];
    dp[0][1] = 0;
    dp[0][2] = 0;
    for (int i = 1; i < n; i++) {
        dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3] - prices[i], dp[i - 1][1] - prices[i]));
        dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);
        dp[i][2] = dp[i - 1][0] + prices[i];
        dp[i][3] = dp[i - 1][2];
    }
    return max(dp[n - 1][3], max(dp[n - 1][1], dp[n - 1][2]));
    }
};

题3:

指路:714. 买卖股票的最佳时机含手续费 - 力扣(LeetCode)
思路与代码: 

本题与股票Ⅱ非常相似,不同在于需要付手续费。一买一卖为一笔交易,当卖出的时候需要减去手续费,再与保持卖出状态的dp数组取较大值得到非持股状态的值。其余都很好写。代码如下:

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
    vector<vector<int>> dp(prices.size(), vector<int>(2, 0));
    if (prices.size() == 0) return 0;
    dp[0][0] = -prices[0];
    dp[0][1] = 0;
    for (int i = 1; i < prices.size(); 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] - fee);  // 非持股
    }
    return dp[prices.size() - 1][1];
    } 
};

股票问题总结:

在买卖股票最佳时机系列问题中,我认为最需要弄清的是几个状态,哪几个状态需要再细分讨论,哪几个状态可以合并处理,在这六个题中体现的淋漓尽致。其次还需要明确最后的返回值,因为卖出股票后手头的资金一定比买入股票后资金多,所以手头资金的最大值应该返回卖出股票状态下的值。然后就是dp数组的初始化需要根据情况而定,不可泛泛而谈初始为0或1。最后需要注意的是在做题过程中需要牢记dp数组的含义。那么到这里,股票问题就暂时告一段落。

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
第二十二算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

R.S.G.久夏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值