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/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
- class Solution:
- def monotoneIncreasingDigits(self, n: int) -> int:
- a = list(str(n))
- for i in range(len(a) - 1, 0, -1) :
- if int (a[i - 1]) > int (a[i]):
- a[i - 1] = str(int(a[i - 1]) - 1)
- a[i:] = '9' * (len(a) - i)
- return int ("".join(a))
贪心思路:从前向后遍历的话,遇到strNum[i - 1] > strNum[i]的情况,让strNum[i - 1]减一,但此时如果strNum[i - 1]减一了,可能又小于strNum[i - 2]。
- class Solution {
- public:
- int monotoneIncreasingDigits(int n) {
- string strNum = to_string(n);
- // flag用来标记赋值9从哪里开始
- // 设置为这个默认值,为了防止第二个for循环在flag没有被复制的情况下执行
- int flag = strNum.size();
- for (int i = strNum.size() - 1; i > 0; i--) {
- if (strNum[i - 1] > strNum[i]) {
- flag = i;
- strNum[i - 1]--;
- }
- }
- for (int i = flag; i < strNum.size(); i++) {
- strNum[i] = '9';
- }
- return stoi(strNum);
- }
- };
三、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
直接贪心
- class Solution {
- public:
- int maxProfit(vector<int>& prices) {
- int low = INT_MAX;
- int res = 0;
- for (int i = 0; i < prices.size(); i++) {
- low = min(prices[i], low);
- res = max(res, prices[i] - low);
- }
- return res;
- }
- };
动态规划:
dp[i][0] 表示第i天持有股票所得现金。dp[i][1] 表示第i天不持有股票所得最多现金
- class Solution {
- // 动态规划
- public:
- int maxProfit(vector<int>& prices) {
- if(prices.size() == 0) return 0;
- vector<vector<int>> dp(prices.size(), vector<int>(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][0], -prices[i]);
- dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
- }
- return dp[prices.size() - 1][1];
- }
- };
(二)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
- class Solution {
- // 动态规划
- //dp[i][0] 表示第i天持有股票所得现金。dp[i][1] 表示第i天不持有股票所得最多现金
-
- public:
- int maxProfit(vector<int>& prices) {
- if (prices.size() == 0) return 0;
- vector<vector<int>> dp(prices.size(), vector<int>(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];
- }
- };
(三)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
- class Solution {
- public:
- int maxProfit(vector<int>& prices, int fee) {
- if (prices.size() == 0) return 0;
- vector<vector<int>> dp(prices.size(), vector<int>(2));
- dp[0][1] = 0;
- dp[0][0] = -prices[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] - fee, dp[i - 1][1]);
- }
- return dp[prices.size() - 1][1];
- }
- };
四、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。
- /**
- * Definition for a binary tree node.
- * struct TreeNode {
- * int val;
- * TreeNode *left;
- * TreeNode *right;
- * TreeNode() : val(0), left(nullptr), right(nullptr) {}
- * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
- * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
- * };
- */
- // 版本一
- class Solution {
- private:
- int result;
- int traversal(TreeNode* cur) {
-
- // 空节点,该节点有覆盖
- if (cur == NULL) return 2;
-
- int left = traversal(cur->left); // 左
- int right = traversal(cur->right); // 右
-
- // 情况1
- // 左右节点都有覆盖
- if (left == 2 && right == 2) return 0;
-
- // 情况2
- // left == 0 && right == 0 左右节点无覆盖
- // left == 1 && right == 0 左节点有摄像头,右节点无覆盖
- // left == 0 && right == 1 左节点有无覆盖,右节点摄像头
- // left == 0 && right == 2 左节点无覆盖,右节点覆盖
- // left == 2 && right == 0 左节点覆盖,右节点无覆盖
- if (left == 0 || right == 0) {
- result++;
- return 1;
- }
-
- // 情况3
- // left == 1 && right == 2 左节点有摄像头,右节点有覆盖
- // left == 2 && right == 1 左节点有覆盖,右节点有摄像头
- // left == 1 && right == 1 左右节点都有摄像头
- // 其他情况前段代码均已覆盖
- if (left == 1 || right == 1) return 2;
-
- // 以上代码我没有使用else,主要是为了把各个分支条件展现出来,这样代码有助于读者理解
- // 这个 return -1 逻辑不会走到这里。
- return -1;
- }
-
- public:
- int minCameraCover(TreeNode* root) {
- result = 0;
- // 情况4
- if (traversal(root) == 0) { // root 无覆盖
- result++;
- }
- return result;
- }
- };
- // 版本二
- class Solution {
- private:
- int result;
- int traversal(TreeNode* cur) {
- if (cur == NULL) return 2;
- int left = traversal(cur->left); // 左
- int right = traversal(cur->right); // 右
- if (left == 2 && right == 2) return 0;
- else if (left == 0 || right == 0) {
- result++;
- return 1;
- } else return 2;
- }
- public:
- int minCameraCover(TreeNode* root) {
- result = 0;
- if (traversal(root) == 0) { // root 无覆盖
- result++;
- }
- return result;
- }
- };
五、总结
贪心专题:
今日总结:
用动态规划的思路做了三道股票题,还附加一道博弈题
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]) 是 奇数
- class Solution {
- // 区间DP问题
- // dp[left][right] 为考虑区间[left,right],在双方都做最好选择的情况下,先手与后手的最大得分差值为多少
- // 转移方程:dp[i][j]=max(piles[i]-dp[i+1][j],piles[j]-dp[i][j-1]);
-
- public:
- bool stoneGame(vector<int>& piles) {
- // 2 <= piles.length <= 500
- // if (piles.size() == 1) return true;
-
- vector<vector<int>> dp(piles.size(), vector<int>(piles.size()));
- // 初始化 —— 当只有一个数时,先手必赢
- for (int i = 0; i < piles.size(); i++) dp[i][i] = piles[i];
-
- // 枚举区间长度
- for (int i = 1; i < piles.size(); i++) {
- // 枚举左端点
- for (int j = 0; j < piles.size() - i; j ++) {
- dp[j][j + i] = max (piles[j] - dp[j + 1][j + i],
- piles[i + j] - dp[j][j + i - 1]);
- }
- }
- return dp[0][piles.size() - 1] > 0;
- }
- };
-
- // https://www.ngui.cc/el/1315810.html?action=onClick 石子游戏专题
标签:right,return,int,随想录,二叉树,prices,第三十六,dp,left
From: https://www.cnblogs.com/ucaszym/p/17144348.html