首页 > 编程语言 >代码随想录算法训练营第三十六天 | 738.单调递增的数字,714. 买卖股票的最佳时机含手续费,968.监控二叉树,总结

代码随想录算法训练营第三十六天 | 738.单调递增的数字,714. 买卖股票的最佳时机含手续费,968.监控二叉树,总结

时间:2023-02-22 15:03:42浏览次数:58  
标签:right return int 随想录 二叉树 prices 第三十六 dp left

Day36 周日休息~

一、参考资料

单调递增的数字

https://programmercarl.com/0738.%E5%8D%95%E8%B0%83%E9%80%92%E5%A2%9E%E7%9A%84%E6%95%B0%E5%AD%97.html

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

https://programmercarl.com/0714.%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E7%9A%84%E6%9C%80%E4%BD%B3%E6%97%B6%E6%9C%BA%E5%90%AB%E6%89%8B%E7%BB%AD%E8%B4%B9.html

监控二叉树

https://programmercarl.com/0968.%E7%9B%91%E6%8E%A7%E4%BA%8C%E5%8F%89%E6%A0%91.html

总结

https://programmercarl.com/%E8%B4%AA%E5%BF%83%E7%AE%97%E6%B3%95%E6%80%BB%E7%BB%93%E7%AF%87.html

二、LeetCode738.单调递增的数字

https://leetcode.cn/problems/monotone-increasing-digits/description/

当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是 单调递增的。
给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。

示例 1:
输入: n = 10 输出: 9
示例 2:
输入: n = 1234 输出: 1234
示例 3:
输入: n = 332 输出: 299
提示:
0 <= n <= 10^9
  1. class Solution:
  2. def monotoneIncreasingDigits(self, n: int) -> int:
  3. a = list(str(n))
  4. for i in range(len(a) - 1, 0, -1) :
  5. if int (a[i - 1]) > int (a[i]):
  6. a[i - 1] = str(int(a[i - 1]) - 1)
  7. a[i:] = '9' * (len(a) - i)
  8. return int ("".join(a))

贪心思路:从前向后遍历的话,遇到strNum[i - 1] > strNum[i]的情况,让strNum[i - 1]减一,但此时如果strNum[i - 1]减一了,可能又小于strNum[i - 2]。

  1. class Solution {
  2. public:
  3. int monotoneIncreasingDigits(int n) {
  4. string strNum = to_string(n);
  5. // flag用来标记赋值9从哪里开始
  6. // 设置为这个默认值,为了防止第二个for循环在flag没有被复制的情况下执行
  7. int flag = strNum.size();
  8. for (int i = strNum.size() - 1; i > 0; i--) {
  9. if (strNum[i - 1] > strNum[i]) {
  10. flag = i;
  11. strNum[i - 1]--;
  12. }
  13. }
  14. for (int i = flag; i < strNum.size(); i++) {
  15. strNum[i] = '9';
  16. }
  17. return stoi(strNum);
  18. }
  19. };

三、LeetCode714. 买卖股票的最佳时机含手续费

(一)LeetCode121. 买卖股票的最佳时机

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例 1:
输入:[7,1,5,3,6,4] 输出:5 解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入:prices = [7,6,4,3,1] 输出:0 解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
提示:
1 <= prices.length <= 10^5
0 <= prices[i] <= 10^4

直接贪心

  1. class Solution {
  2. public:
  3. int maxProfit(vector<int>& prices) {
  4. int low = INT_MAX;
  5. int res = 0;
  6. for (int i = 0; i < prices.size(); i++) {
  7. low = min(prices[i], low);
  8. res = max(res, prices[i] - low);
  9. }
  10. return res;
  11. }
  12. };

动态规划:

dp[i][0] 表示第i天持有股票所得现金。dp[i][1] 表示第i天不持有股票所得最多现金

  1. class Solution {
  2. // 动态规划
  3. public:
  4. int maxProfit(vector<int>& prices) {
  5. if(prices.size() == 0) return 0;
  6. vector<vector<int>> dp(prices.size(), vector<int>(2));
  7. dp[0][0] = -prices[0];
  8. dp[0][1] = 0;
  9. for (int i = 1; i < prices.size(); i++) {
  10. dp[i][0] = max(dp[i - 1][0], -prices[i]);
  11. dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
  12. }
  13. return dp[prices.size() - 1][1];
  14. }
  15. };

(二)LeetCode122. 买卖股票的最佳时机 II

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/description/

给你一个整数数组 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 。
示例 2:
输入:prices = [1,2,3,4,5] 输出:4 解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。 总利润为 4 。
示例 3:
输入:prices = [7,6,4,3,1] 输出:0 解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。

提示:
1 <= prices.length <= 3 * 10^4
0 <= prices[i] <= 10^4
  1. class Solution {
  2. // 动态规划
  3. //dp[i][0] 表示第i天持有股票所得现金。dp[i][1] 表示第i天不持有股票所得最多现金
  4. public:
  5. int maxProfit(vector<int>& prices) {
  6. if (prices.size() == 0) return 0;
  7. vector<vector<int>> dp(prices.size(), vector<int>(2));
  8. dp[0][0] = -prices[0];
  9. dp[0][1] = 0;
  10. for (int i = 1; i < prices.size(); i++) {
  11. dp[i][0] = max (dp[i - 1][1] - prices[i], dp[i - 1][0]);
  12. dp[i][1] = max (dp[i - 1][0] + prices[i], dp[i - 1][1]);
  13. }
  14. return dp[prices.size() - 1][1];
  15. }
  16. };

(三)LeetCode714. 买卖股票的最佳时机含手续费

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/description/

给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值。
注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

示例 1:
输入:prices = [1, 3, 2, 8, 4, 9], fee = 2 输出:8 解释:能够达到的最大利润: 在此处买入 prices[0] = 1 在此处卖出 prices[3] = 8 在此处买入 prices[4] = 4 在此处卖出 prices[5] = 9 总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8
示例 2:
输入:prices = [1,3,7,5,10,3], fee = 3 输出:6
提示:
1 <= prices.length <= 5 * 10^4
1 <= prices[i] < 5 * 10^4
0 <= fee < 5 * 10^4
  1. class Solution {
  2. public:
  3. int maxProfit(vector<int>& prices, int fee) {
  4. if (prices.size() == 0) return 0;
  5. vector<vector<int>> dp(prices.size(), vector<int>(2));
  6. dp[0][1] = 0;
  7. dp[0][0] = -prices[0];
  8. for (int i = 1; i < prices.size(); i++) {
  9. dp[i][0] = max(dp[i - 1][1] - prices[i], dp[i - 1][0]);
  10. dp[i][1] = max(dp[i - 1][0] + prices[i] - fee, dp[i - 1][1]);
  11. }
  12. return dp[prices.size() - 1][1];
  13. }
  14. };

四、LeetCode968.监控二叉树(难题-pass)

https://leetcode.cn/problems/binary-tree-cameras/description/

示例一:

示例二:

给定一个二叉树,我们在树的节点上安装摄像头。
节点上的每个摄影头都可以监视 其父对象、自身及其直接子对象。
计算监控树的所有节点所需的最小摄像头数量。

示例 1:
输入:[0,0,null,0,0] 输出:1 解释:如图所示,一台摄像头足以监控所有节点。
示例 2:
输入:[0,0,null,0,null,0,null,null,0] 输出:2 解释:需要至少两个摄像头来监视树的所有节点。 上图显示了摄像头放置的有效位置之一。
提示:
给定树的节点数的范围是 [1, 1000]。
每个节点的值都是 0。
  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. // 版本一
  13. class Solution {
  14. private:
  15. int result;
  16. int traversal(TreeNode* cur) {
  17. // 空节点,该节点有覆盖
  18. if (cur == NULL) return 2;
  19. int left = traversal(cur->left); // 左
  20. int right = traversal(cur->right); // 右
  21. // 情况1
  22. // 左右节点都有覆盖
  23. if (left == 2 && right == 2) return 0;
  24. // 情况2
  25. // left == 0 && right == 0 左右节点无覆盖
  26. // left == 1 && right == 0 左节点有摄像头,右节点无覆盖
  27. // left == 0 && right == 1 左节点有无覆盖,右节点摄像头
  28. // left == 0 && right == 2 左节点无覆盖,右节点覆盖
  29. // left == 2 && right == 0 左节点覆盖,右节点无覆盖
  30. if (left == 0 || right == 0) {
  31. result++;
  32. return 1;
  33. }
  34. // 情况3
  35. // left == 1 && right == 2 左节点有摄像头,右节点有覆盖
  36. // left == 2 && right == 1 左节点有覆盖,右节点有摄像头
  37. // left == 1 && right == 1 左右节点都有摄像头
  38. // 其他情况前段代码均已覆盖
  39. if (left == 1 || right == 1) return 2;
  40. // 以上代码我没有使用else,主要是为了把各个分支条件展现出来,这样代码有助于读者理解
  41. // 这个 return -1 逻辑不会走到这里。
  42. return -1;
  43. }
  44. public:
  45. int minCameraCover(TreeNode* root) {
  46. result = 0;
  47. // 情况4
  48. if (traversal(root) == 0) { // root 无覆盖
  49. result++;
  50. }
  51. return result;
  52. }
  53. };
  1. // 版本二
  2. class Solution {
  3. private:
  4. int result;
  5. int traversal(TreeNode* cur) {
  6. if (cur == NULL) return 2;
  7. int left = traversal(cur->left); // 左
  8. int right = traversal(cur->right); // 右
  9. if (left == 2 && right == 2) return 0;
  10. else if (left == 0 || right == 0) {
  11. result++;
  12. return 1;
  13. } else return 2;
  14. }
  15. public:
  16. int minCameraCover(TreeNode* root) {
  17. result = 0;
  18. if (traversal(root) == 0) { // root 无覆盖
  19. result++;
  20. }
  21. return result;
  22. }
  23. };

五、总结

贪心专题:

今日总结:

用动态规划的思路做了三道股票题,还附加一道博弈题

https://www.ngui.cc/el/1315810.html?action=onClick

LeetCode 877. 石子游戏

https://leetcode.cn/problems/stone-game/description/?page=2

Alice 和 Bob 用几堆石子在做游戏。一共有偶数堆石子, 排成一行;每堆都有 正 整数颗石子,数目为 piles[i] 。
游戏以谁手中的石子最多来决出胜负。石子的 总数 是 奇数 ,所以没有平局。
Alice 和 Bob 轮流进行, Alice 先开始 。 每回合,玩家从行的 开始 或 结束 处取走整堆石头。 这种情况一直持续到没有更多的石子堆为止,此时手中 石子最多 的玩家 获胜 。
假设 Alice 和 Bob 都发挥出最佳水平,当 Alice 赢得比赛时返回 true ,当 Bob 赢得比赛时返回 false 。

示例 1:
输入:piles = [5,3,4,5] 输出:true 解释: Alice 先开始,只能拿前 5 颗或后 5 颗石子 。 假设他取了前 5 颗,这一行就变成了 [3,4,5] 。 如果 Bob 拿走前 3 颗,那么剩下的是 [4,5],Alice 拿走后 5 颗赢得 10 分。 如果 Bob 拿走后 5 颗,那么剩下的是 [3,4],Alice 拿走后 4 颗赢得 9 分。 这表明,取前 5 颗石子对 Alice 来说是一个胜利的举动,所以返回 true 。
示例 2:
输入:piles = [3,7,2,3] 输出:true
提示:
2 <= piles.length <= 500
piles.length 是 偶数
1 <= piles[i] <= 500
sum(piles[i]) 是 奇数
  1. class Solution {
  2. // 区间DP问题
  3. // dp[left][right] 为考虑区间[left,right],在双方都做最好选择的情况下,先手与后手的最大得分差值为多少
  4. // 转移方程:dp[i][j]=max(piles[i]-dp[i+1][j],piles[j]-dp[i][j-1]);
  5. public:
  6. bool stoneGame(vector<int>& piles) {
  7. // 2 <= piles.length <= 500
  8. // if (piles.size() == 1) return true;
  9. vector<vector<int>> dp(piles.size(), vector<int>(piles.size()));
  10. // 初始化 —— 当只有一个数时,先手必赢
  11. for (int i = 0; i < piles.size(); i++) dp[i][i] = piles[i];
  12. // 枚举区间长度
  13. for (int i = 1; i < piles.size(); i++) {
  14. // 枚举左端点
  15. for (int j = 0; j < piles.size() - i; j ++) {
  16. dp[j][j + i] = max (piles[j] - dp[j + 1][j + i],
  17. piles[i + j] - dp[j][j + i - 1]);
  18. }
  19. }
  20. return dp[0][piles.size() - 1] > 0;
  21. }
  22. };
  23. // https://www.ngui.cc/el/1315810.html?action=onClick 石子游戏专题

https://leetcode.cn/problems/stone-game/solutions/797965/shi-zi-you-xi-dong-tai-gui-hua-qu-jian-d-5ra8/ 解题思路参考

标签:right,return,int,随想录,二叉树,prices,第三十六,dp,left
From: https://www.cnblogs.com/ucaszym/p/17144348.html

相关文章