代码随想录算法训练营第三十五天丨 贪心算法part06

738.单调递增的数字

思路

暴力解法

题意很简单,那么首先想的就是暴力解法了【超时】。

贪心算法

题目要求小于等于N的最大单调递增的整数,那么拿一个两位的数字来举例。

例如:98,一旦出现strNum[i - 1] > strNum[i]的情况(非单调递增),首先想让strNum[i - 1]--,然后strNum[i]给为9,这样这个整数就是89,即小于98的最大的单调递增整数。

这一点如果想清楚了,这道题就好办了。

此时是从前向后遍历还是从后向前遍历呢?

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

这么说有点抽象,举个例子,数字:332,从前向后遍历的话,那么就把变成了329,此时2又小于了第一位的3了,真正的结果应该是299。

那么从后向前遍历,就可以重复利用上次比较得出的结果了,从后向前遍历332的数值变化为:332 -> 329 -> 299

确定了遍历顺序之后,那么此时局部最优就可以推出全局,找不出反例,试试贪心。

代码如下:

class Solution {public int monotoneIncreasingDigits(int n) {char[] chars = String.valueOf(n).toCharArray();int flag = chars.length;for (int i = chars.length-1; i > 0 ; i--) {if (chars[i] < chars[i-1]){chars[i-1]--;flag=i-1;}}for (int i = flag+1; i < chars.length; i++) {chars[i]='9';}return Integer.parseInt(String.valueOf(chars));}
}
  • 时间复杂度:O(n),n 为数字长度
  • 空间复杂度:O(n),需要一个字符串,转化为字符串操作更方便

总结

本题只要想清楚个例,例如98,一旦出现strNum[i - 1] > strNum[i]的情况(非单调递增),首先想让strNum[i - 1]减一,strNum[i]赋值9,这样这个整数就是89。就可以很自然想到对应的贪心解法了。

想到了贪心,还要考虑遍历顺序,只有从后向前遍历才能重复利用上次比较的结果。

最后代码实现的时候,也需要一些技巧,例如用一个flag来标记从哪里开始赋值9。


968.监控二叉树

思路

这道题目首先要想,如何放置,才能让摄像头最小的呢?

从题目中示例,其实可以得到启发,我们发现题目示例中的摄像头都没有放在叶子节点上!

这是很重要的一个线索,摄像头可以覆盖上中下三层,如果把摄像头放在叶子节点上,就浪费的一层的覆盖。

所以把摄像头放在叶子节点的父节点位置,才能充分利用摄像头的覆盖面积。

为啥要从叶子节点看呢?

因为头结点放不放摄像头也就省下一个摄像头, 叶子节点放不放摄像头省下了的摄像头数量是指数阶别的。

所以我们要从下往上看,局部最优:让叶子节点的父节点安摄像头,所用摄像头最少,整体最优:全部摄像头数量所用最少!

局部最优推出全局最优,找不出反例,那么就按照贪心来!

此时,大体思路就是从低到上,先给叶子节点父节点放个摄像头,然后隔两个节点放一个摄像头,直至到二叉树头结点。

此时这道题目还有两个难点:

  1. 二叉树的遍历
  2. 如何隔两个节点放一个摄像头

确定遍历顺序

在二叉树中如何从低向上推导呢?

可以使用后序遍历也就是左右中的顺序,这样就可以在回溯的过程中从下到上进行推导了。

后序遍历代码如下:

int traversal(TreeNode* cur) {// 空节点,该节点有覆盖if (终止条件) return ;int left = traversal(cur->left);    // 左int right = traversal(cur->right);  // 右逻辑处理                            // 中return ;
}

注意在以上代码中我们取了左孩子的返回值,右孩子的返回值,即left 和 right, 以后推导中间节点的状态

如何隔两个节点放一个摄像头

此时需要状态转移的公式,大家不要和动态的状态转移公式混到一起,本题状态转移没有择优的过程,就是单纯的状态转移!

来看看这个状态应该如何转移,先来看看每个节点可能有几种状态:

有如下三种:

  • 该节点无覆盖
  • 本节点有摄像头
  • 本节点有覆盖

我们分别有三个数字来表示:

  • 0:该节点无覆盖
  • 1:本节点有摄像头
  • 2:本节点有覆盖

大家应该找不出第四个节点的状态了。

一些同学可能会想有没有第四种状态:本节点无摄像头,其实无摄像头就是 无覆盖 或者 有覆盖的状态,所以一共还是三个状态。

因为在遍历树的过程中,就会遇到空节点,那么问题来了,空节点究竟是哪一种状态呢? 空节点表示无覆盖? 表示有摄像头?还是有覆盖呢?

回归本质,为了让摄像头数量最少,我们要尽量让叶子节点的父节点安装摄像头,这样才能摄像头的数量最少。

那么空节点不能是无覆盖的状态,这样叶子节点就要放摄像头了,空节点也不能是有摄像头的状态,这样叶子节点的父节点就没有必要放摄像头了,而是可以把摄像头放在叶子节点的爷爷节点上。

所以空节点的状态只能是有覆盖,这样就可以在叶子节点的父节点放摄像头了

接下来就是递推关系。

那么递归的终止条件应该是遇到了空节点,此时应该返回2(有覆盖),原因上面已经解释过了。

代码如下:

// 空节点,该节点有覆盖
if (cur == NULL) return 2;

递归的函数,以及终止条件已经确定了,再来看单层逻辑处理。

主要有如下四类情况:

  • 情况1:左右节点都有覆盖

左孩子有覆盖,右孩子有覆盖,那么此时中间节点应该就是无覆盖的状态了。

如图:

968.监控二叉树2

代码如下:

// 左右节点都有覆盖
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 左节点覆盖,右节点无覆盖

这个不难理解,毕竟有一个孩子没有覆盖,父节点就应该放摄像头。

此时摄像头的数量要加一,并且return 1,代表中间节点放摄像头。

代码如下:

if (left == 0 || right == 0) {result++;return 1;
}
  • 情况3:左右节点至少有一个有摄像头

如果是以下情况,其实就是 左右孩子节点有一个有摄像头了,那么其父节点就应该是2(覆盖的状态)

  • left == 1 && right == 2 左节点有摄像头,右节点有覆盖
  • left == 2 && right == 1 左节点有覆盖,右节点有摄像头
  • left == 1 && right == 1 左右节点都有摄像头

代码如下:

if (left == 1 || right == 1) return 2;

从这个代码中,可以看出,如果left == 1, right == 0 怎么办?其实这种条件在情况2中已经判断过了,如图:

968.监控二叉树1

这种情况也是大多数同学容易迷惑的情况。

  1. 情况4:头结点没有覆盖

以上都处理完了,递归结束之后,可能头结点 还有一个无覆盖的情况,如图:

968.监控二叉树3

所以递归结束之后,还要判断根节点,如果没有覆盖,result++,代码如下:

int minCameraCover(TreeNode* root) {result = 0;if (traversal(root) == 0) { // root 无覆盖result++;}return result;
}

以上四种情况我们分析完了,

整体代码如下:

class Solution {int res = 0;public int minCameraCover(TreeNode root) {// 对根节点的状态做检验,防止根节点是无覆盖状态if (findCamer(root) == 0){res++;}return res;}/**节点的状态值:0 表示无覆盖1 表示 有摄像头2 表示有覆盖后序遍历,根据左右节点的情况,来判读 自己的状态*/int findCamer(TreeNode node){//终止条件if (node == null){return 2;//当结点为空时返回有覆盖的状态2}//遍历顺序,左右中int left = findCamer(node.left);int right = findCamer(node.right);//当左右子节点都为有覆盖时,当前结点为无覆盖 0if (left==2 && right==2){return 0;}//当前结点的左右子节点只要有一个为 无覆盖,则当前结点添加摄像头,返回 有摄像头1if (left == 0 || right == 0){res++;return 1;}//当左右子节点 有一个为 有摄像头时,当前结点为 有覆盖if (left == 1 || right ==1){return 2;}return -1;}
}

贪心算法暂时over!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/151255.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在VMware Workstation Pro安装win7

1.下载 地址 2.创建虚拟机 3.选择需要安装的系统镜像 4.选择系统版本 通常情况下选择 Windows 7 Ultimate 旗舰版&#xff0c;点击下一步&#xff0c;若提示产品密钥&#xff0c;则忽略 5.虚拟机命名 虚拟机保存位置保持默认即可&#xff0c;如果有需求可以更换位置 6…

C++之特殊类的设计

目录 一、单例模式 1、设计模式 2、单例模式 1、饿汉模式 2、懒汉模式 3、单例对象的释放问题 二、设计一个不能被拷贝的类 三、设计一个只能在堆上创建对象的类 四、设计一个只能在栈上创建对象的类 五、设计一个不能被继承的类 一、单例模式 1、设计模式 概念&am…

LLMs之ChatGLM3:ChatGLM3/ChatGLM3-6B的简介(多阶段增强+多模态理解+AgentTuning技术)、安装、使用方法之详细攻略

LLMs之ChatGLM3&#xff1a;ChatGLM3/ChatGLM3-6B的简介(多阶段增强多模态理解AgentTuning技术)、安装、使用方法之详细攻略 导读&#xff1a;2023年10月27日&#xff0c;智谱AI在2023中国计算机大会上推出了全自研的第三代基座大模型ChatGLM3及其相关系列产品&#xff0c;这是…

科技联众,互利共赢 | 卡驰科技(深圳)有限公司CEO张倍铭博士到访拓世科技集团,共探跨境电商,海外拓展无限可能

在创新的浪潮中&#xff0c;人工智能以其强大的能量持续推动着各行各业的进步&#xff0c;拓世科技集团作为人工智能领域的引领者&#xff0c;通过不断合作和交流&#xff0c;与众多领域的佼佼者共同探讨数字时代的无限可能。卡驰科技&#xff08;深圳&#xff09;有限公司作为…

代码随想录打卡第五十三天|309.最佳买卖股票时机含冷冻期 ● 714.买卖股票的最佳时机含手续费

309.最佳买卖股票时机含冷冻期 题目&#xff1a; 给定一个整数数组prices&#xff0c;其中第 prices[i] 表示第 i 天的股票价格 。​ 设计一个算法计算出最大利润。在满足以下约束条件下&#xff0c;你可以尽可能地完成更多的交易&#xff08;多次买卖一支股票&#xff09;: 卖…

轻量封装WebGPU渲染系统示例<2>-彩色立方体(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/version-1.01/src/voxgpu/sample/VertColorCube.ts 此示例渲染系统实现的特性: 1. 用户态与系统态隔离。 2. 高频调用与低频调用隔离。 3. 面向用户的易用性封装。 4. 渲染数据和渲染机制分离。 5. …

2023年清洁电器行业数据分析:洗地机市场规模持续倍增,进入赛点

洗地机作为清洁电器领域的明星品类&#xff0c;正在成为继扫地机器人之后拉动清洁电器市场大盘的又一核心动力。 在清洁电器领域&#xff0c;扫地机器人、洗地机和吸尘器是三大热门品类。截至今年9月份&#xff0c;根据鲸参谋平台的数据显示&#xff0c;吸尘器的规模继续大幅下…

简化路径(C++解法)

题目 给你一个字符串 path &#xff0c;表示指向某一文件或目录的 Unix 风格 绝对路径 &#xff08;以 / 开头&#xff09;&#xff0c;请你将其转化为更加简洁的规范路径。 在 Unix 风格的文件系统中&#xff0c;一个点&#xff08;.&#xff09;表示当前目录本身&#xff1…

uni-app:实现picker下拉列表的默认值设置

效果 分析 1、在data中将index8的初始值设置为-1&#xff0c;表示未选择任何选项&#xff1a; index8: -1, //选择的下拉列表下标 2、在bindPickerChange8事件处理函数中添加条件判断。如果选择的值是-1&#xff0c;则将this.index8设置为"请输入"&#xff0c;否则将…

Docker 容器服务的注册、发现及Docker安全

目录 Docker容器服务的注册和发现 1、什么是服务注册与发现&#xff1f; 2、什么是consul consul的部署 1、环境准备 2、部署consul服务器 1&#xff09;建立 Consul 服务 2&#xff09;设置代理&#xff0c;在后台启动 consul 服务端 3&#xff09;查看集群信息 4&a…

有线网卡通过无线网卡使其它设备上网

我现在的网络是无线路由器连接公网&#xff0c;电脑上的无线网卡连接路由器使电脑上网&#xff0c;这是完全正常的连接方式。 我现在又有了一台嵌入式设备&#xff0c;它只有有线网口&#xff0c;所以就只能用有线的方式连网&#xff0c;但是我的无线路由器不在电脑旁边&#x…

力扣在线OJ——栈和队列

目录 &#x1f341;一、用两个队列实现栈 &#x1f315;&#xff08;一&#xff09;、题目&#xff08;力扣链接&#xff1a;用队列实现栈 &#xff09; &#x1f315;&#xff08;二&#xff09;、注意 &#x1f315;&#xff08;三&#xff09;、解答 ⭐️1.注意事项 ⭐…