算法训练营(day37)
贪心算法理论基础
贪心的本质是选择每一阶段的局部最优,从而达到全局最优。
举个例子,在规定次数内从一堆钞票里怎么拿面额最大的钞票,根据贪心算法的思想:每次拿最大的就好(够贪就行O(∩_∩)O)
贪心算法一般分为如下四步:
- 将问题分解为若干个子问题
- 找出适合的贪心策略
- 求解每一个子问题的最优解
- 将局部最优解堆叠成全局最优解
总而言之,贪心算法的本质其实就是 找到局部最优解,再比较所有的局部最优,得到全局最优。
738. 单调递增的数字
题目链接:https://leetcode.cn/problems/monotone-increasing-digits/description/
当且仅当每个相邻位数上的数字 x
和 y
满足 x <= y
时,我们称这个整数是单调递增的。
给定一个整数 n
,返回 小于或等于 n
的最大数字,且数字呈 单调递增 。
解题思路
解题过程:贪心
- 以一个 百位数 为例
- 局部最优:比较 百位和十位,如果百位比十位 大,则百位减一,十位取9
- 全局最优:从高到低为依次遍历,得到全局最优解
详细代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Solution { public int monotoneIncreasingDigits(int n) { String s = String.valueOf(n); char[] chars = s.toCharArray(); int start = s.length(); for(int i = s.length() - 2; i >= 0; i--){ if(chars[i] > chars[i + 1]){ chars[i]--; start = i + 1; } } for(int i = start; i < s.length(); i++){ chars[i] = '9'; } return Integer.parseInt(String.valueOf(chars)); } }
|
714. 买卖股票的最佳时机含手续费
题目链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/description/
给定一个整数数组 prices
,其中 prices[i]
表示第 i
天的股票价格 ;整数 fee
代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值。
注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
解题思路
解题过程:
思路一:贪心
局部最优:
- 在当天股票金额+手续费 比 前一天低的时候买入
- 在 当天股票金额 比 买入金额 高时卖出,统计差值
全局最优:只要买入比卖出便宜就赚钱,返回差值
思路二:动态规划
- 定义 0 是 持股(买入),1 是 不持股(售出)
dp
定义 第 i 天的持有现金的情况
- 返回
dp
最大值
详细代码
解法一:贪心
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Solution { public int maxProfit(int[] prices, int fee) { int sum = 0; int buy = prices[0] + fee;
for(int p : prices){ if(p + fee < buy){ buy = p + fee; }else if(p > buy){ sum += p - buy; buy = p; } } return sum; } }
|
解法二:动态规划
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Solution { public int maxProfit(int[] prices, int fee) { int len = prices.length; int[][] dp = new int[len][2]; dp[0][0] = -prices[0] - fee;
for(int i = 1; i < len; i++){ dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i] - fee); dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]); } return Math.max(dp[len - 1][0], dp[len - 1][1]); } }
|
968. 监控二叉树
题目链接:https://leetcode.cn/problems/binary-tree-cameras/description/
给定一个二叉树,我们在树的节点上安装摄像头。
节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。
计算监控树的所有节点所需的最小摄像头数量。
解题思路
解题过程:贪心
定义节点状态:
- 0:该节点无覆盖
- 1:本节点有摄像头
- 2:本节点有覆盖
通过递归,定义判断条件
对根节点进行判断,避免漏检
返回摄像头数量
详细代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
class Solution { int result = 0; public int minCameraCover(TreeNode root) { if(minCamera(root) == 0){ result++; } return result; }
public int minCamera(TreeNode node){ if(node == null){ return 2; } int left = minCamera(node.left); int right = minCamera(node.right);
if(left == 2 && right == 2){ return 0; }else if(left == 0 || right == 0){ result++; return 1; }else{ return 2; } } }
|