【leetcode热题】分发糖果

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

示例 1:

输入:ratings = [1,0,2]
输出:5
解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。

示例 2:

输入:ratings = [1,2,2]
输出:4
解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。

解法一

根据题目,首先每个小朋友会至少有一个糖。

如果当前小朋友的 rating 比后一个小朋友的小,那么后一个小朋友的糖肯定是当前小朋友的糖加 1

比如 ration = [ 5, 6, 7] ,那么三个小朋友的糖就依次是 1 2 3

如果当前小朋友的 rating 比后一个小朋友的大,那么理论上当前小朋友的糖要比后一个的小朋友的糖多,但此时后一个小朋友的糖还没有确定,怎么办呢?

参考 32题 的解法五,利用正着遍历,再倒着遍历的思想。

首先我们正着遍历一次,只考虑当前小朋友的 rating 比后一个小朋友的小的情况。

接着再倒着遍历依次,继续考虑当前小朋友的 rating 比后一个小朋友的小的情况。因为之前已经更新过一次糖果了,此时后一个小朋友的糖如果已经比当前小朋友的糖多了,就不需要进行更新了。

举个例子

初始化每人一个糖
1 2 3 2 1 4
- - - - - -.只考虑当前小朋友的 rating 比后一个小朋友的小的情况,后一个小朋友的糖是当前小朋友的糖加 1。
1 < 2
1 2 3 2 1 4
- - - - - --   2 < 3
1 2 3 2 1 4
- - - - - -- --3 > 2 不考虑2 > 1 不考虑1 < 4
1 2 3 2 1 4
- - - - - -- -     --倒过来重新进行
继续考虑当前小朋友的 rating 比后一个小朋友的小的情况。此时后一个小朋友的糖如果已经比当前小朋友的糖多了,就不需要进行更新。
4 1 2 3 2 1
- - - - - -
-     - --4 > 1 不考虑1 < 2
4 1 2 3 2 1
- - - - - -
-   - - --    2 < 3,3 的糖果已经比 2 的多了,不需要考虑3 > 2,不考虑2 > 1,不考虑所以最终的糖的数量就是上边的 - 的和。

代码的话,我们用一个 candies 数组保存当前的分配情况。

public int candy(int[] ratings) {int n = ratings.length;int[] candies = new int[n];//每人发一个糖for (int i = 0; i < n; i++) {candies[i] = 1;}//正着进行for (int i = 0; i < n - 1; i++) {//当前小朋友的 rating 比后一个小朋友的小,后一个小朋友的糖是当前小朋友的糖加 1。if (ratings[i] < ratings[i + 1]) {candies[i + 1] = candies[i] + 1;}}//倒着进行//下标顺序就变成了 i i-1 i-2 i-3 ... 0//当前就是第 i 个,后一个就是第 i - 1 个for (int i = n - 1; i > 0; i--) {//当前小朋友的 rating 比后一个小朋友的小if (ratings[i] < ratings[i - 1]) {//后一个小朋友的糖果树没有前一个的多,就更新后一个等于前一个加 1if (candies[i - 1] <= candies[i]) {candies[i - 1] = candies[i] + 1;}}}//计算糖果总和int sum = 0;for (int i = 0; i < n; i++) {sum += candies[i];}return sum;
}

时间复杂度:O(n)。

空间复杂度:O(n)。

解法二

参考 这里。

解法一中,考虑到

如果当前小朋友的 rating 比后一个小朋友的大,那么理论上当前小朋友的糖要比后一个的小朋友的糖多,但此时后一个小朋友的糖还没有确定,怎么办呢?

之前采用了倒着遍历一次的方式进行了解决,这里再考虑另外一种解法。

考虑下边的情况。

对于第 2 个 rating 4,它比后一个 rating 要大,所以要取决于再后边的 rating,一直走到 2,也就是山底,此时对应的糖果数是 1,然后往后走,走回山顶,糖果数一次加 1,也就是到 rating 4 时,糖果数就是 3 了。

再一般化,山顶的糖果数就等于从左边的山底或右边的山底依次加 1 。

所以我们的算法只需要记录山顶,然后再记录下坡的高度,下坡的高度刚好是一个等差序列可以直接用公式求和。而山顶的糖果数,取决于左边山底到山顶和右边山底到山顶的哪个高度大。

而产生山底可以有两种情况,一种是 rating 产生了增加,如上图。还有一种就是 rating 不再降低,而是持平。

知道了上边的想法,基本上就可以写代码了,每个人写出来的应该都不一样,在 discuss 区也看到了很多不同的写法,下边说一下我的思路。

抽象出四种情况,这里的高度不是 rating 进行相减,而是从山底的 rating 到山顶的 rating 经过的次数。

  1. 左边山底到山顶的高度大,并且右边山底后继续增加。

  2. 左边山底到山顶的高度大,并且右边山底是平坡。

  3. 右边山底到山顶的高度大,并且右边山底后继续增加。

  4. 右边山底到山顶的高度大,并且右边山底是平坡。

有了这四种情况就可以写代码了。

我们用 total 变量记录糖果总和, pre 变量记录前一个小朋友的糖果数。如果当前的 rating 比前一个的 rating 大,那么说明在走上坡,可以把前一个小朋友的糖果数加到 total 中,并且更新 pre 为当前小朋友的糖果数。

如果当前的 rating 比前一个的 rating 小,说明开始走下坡,用 down 变量记录连续多少次下降,此时的 pre 记录的就是从左边山底到山底的高度。当出现平坡或上坡的时候,将所有的下坡的糖果数利用等差公式计算。此外根据 pre 和 down 决定山顶的糖果数。

根据当前是上坡还是平坡,来更新 pre

大框架就是上边的想法了,还有一些边界需要考虑一下,看一下代码。

public int candy(int[] ratings) {int n = ratings.length;int total = 0;int down = 0;int pre = 1;for (int i = 1; i < n; i++) {//当前是在上坡或者平坡if (ratings[i] >= ratings[i - 1]) {//之前出现过了下坡if (down > 0) {//山顶的糖果数大于下降的高度,对应情况 1//将下降的糖果数利用等差公式计算,单独加上山顶if (pre > down) {total += count(down);total += pre;//山顶的糖果数小于下降的高度,对应情况 3,//将山顶也按照等差公式直接计算进去累加} else {total += count(down + 1);}//当前是上坡,对应情况 1 或者 3//更新 pre 等于 2if (ratings[i] > ratings[i - 1]) {pre = 2;//当前是平坡,对应情况 2 或者 4//更新 pre 等于 1} else {pre = 1;}down = 0;//之前没有出现过下坡} else {//将前一个小朋友的糖果数相加total += pre;//如果是上坡更新当前糖果数是上一个的加 1if (ratings[i] > ratings[i - 1]) {pre = pre + 1;//如果是平坡,更新当前糖果数为 1} else {pre = 1;}}} else {down++;}}//判断是否有下坡if (down > 0) {//和之前的逻辑一样进行相加if (pre > down) {total += count(down);total += pre;} else {total += count(down + 1);}//将最后一个小朋友的糖果计算} else {total += pre;}return total;
}//等差数列求和
private int count(int n) {return (1 + n) * n / 2;
}

这个算法相对于解法一的好处就是将空间复杂度从 O(n) 优化到了 O(1)

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

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

相关文章

使用Visual Studio 2022 创建lib和dll并使用

概述&#xff1a;对于一个经常写javaWeb的人来说,使用Visual Studio似乎没什么必要&#xff0c;但是对于使用ffi的人来说&#xff0c;使用c或c编译器&#xff0c;似乎是必不可少的&#xff0c;下面我将讲述如何用Visual Studio 2022 来创建lib和dll&#xff0c;并使用。 静态库…

前端面试 跨域理解

2 实现 2-1 JSONP 实现 2-2 nginx 配置 2-2 vue 开发中 webpack自带跨域 2 -3 下载CORS 插件 或 chrome浏览器配置跨域 2-4 通过iframe 如&#xff1a;aaa.com 中读取bbb.com的localStorage 1)在aaa.com的页面中&#xff0c;在页面中嵌入一个src为bbb.com的iframe&#x…

详细分析Linux内存知识并释放内存

目录 前言1. 基本知识1.1 free1.2 cat /proc/meminfo1.3 slabtop 2. 清空内存 前言 本篇文章主要分析内存 如果是磁盘空间&#xff0c;推荐阅读&#xff1a;服务器出现根目录磁盘满了解决方法 1. 基本知识 在Linux系统中&#xff0c;查看内存的基本知识包括以下几个方面&…

Rabbitmq消息丢失-消费者消息丢失(二)

说明&#xff1a;消费端在处理消息的过程中出现异常&#xff0c;例如&#xff1a;业务逻辑异常&#xff0c;或者消费者被停机&#xff0c;或者网络断开连接等&#xff0c;以上等情况使消息没有得到正确恰当的处理&#xff0c;也会使消息丢失。 分析&#xff1a;分析就是说明中…

第 387 场 LeetCode 周赛题解

A 3069. 将元素分配到两个数组中 I 模拟 class Solution { public:vector<int> resultArray(vector<int> &nums) {vector<int> r1{nums[0]}, r2{nums[1]};for (int i 2; i < nums.size(); i) {if (r1.back() > r2.back())r1.push_back(nums[i]);e…

WPF中如何设置自定义控件(二)

前一篇文章中简要讲解了圆角按钮、圆形按钮的使用,以及在windows.resource和app.resource中设置圆角或圆形按钮的样式。 这篇主要讲解Polygon(多边形)、Ellipse(椭圆)、Path(路径)这三个内容。 Polygon 我们先看一下的源码: namespace System.Windows.Shapes { pu…

Vue3学习记录(三)--- 组合式API之生命周期和模板引用

一、生命周期 1、简介 ​ 生命周期&#xff0c;指的是一个 Vue 实例从创建到销毁的完整阶段&#xff0c;强调的是一个时间段。 ​ 生命周期钩子函数&#xff0c;指的是 Vue 实例提供的内置函数&#xff0c;函数的参数为一个回调函数。这些钩子函数会在实例生命周期的某些固定…

Spring(22) Spring中的9种设计模式

目录 一、简单工厂模式&#xff08;Simple Factory&#xff09;二、工厂方法模式&#xff08;Factory Method&#xff09;三、单例模式&#xff08;Singleton&#xff09;四、适配器模式&#xff08;Adapter&#xff09;五、代理模式&#xff08;Proxy&#xff09;七、观察者模…

【ARM Trace32(劳特巴赫) 高级篇 21 -- SystemTrace ITM 使用介绍】

文章目录 SystemTrace ITMSystemTrace ITM 常用命令Trace Data AnalysisSystemTrace ITM CoreSight ITM (Instrumentation Trace Macrocell) provides the following information: Address, data value and instruction address for selected data cyclesInterrupt event info…

就业班 2401--3.4 Linux Day10--软件管理

一、软件管理 导语&#xff1a; 安装软件 rpm yum 源码安装 ​ 卸载软件 rpm介绍 rpm软件包名称: 软件名称 版本号(主版本、次版本、修订号) 操作系统 -----90%的规律 #有依赖关系,不能自动解决依赖关系。 举例&#xff1a;openssh-6.6.1p1-31.el7.x86_64.rpm 数字前面的是名…

SkyWalking链路追踪上下文TraceContext的追踪身份traceId生成的实现原理剖析

结论先行 SkyWalking 通过字节码增强技术实现&#xff0c;结合依赖注入和控制反转思想&#xff0c;以SkyWalking方式将追踪身份traceId编织到链路追踪上下文TraceContext中。 是不是很有趣&#xff0c;很有意思&#xff01;&#xff01;&#xff01; 实现原理剖析 TraceConte…

Kubernetes基础(二十七)-nodePort/targetPort/port/containerPort/hostPort

1 nodePort/targetPort/port/containerPort 1.1 实现层级 1.2 配置方式 ########service########### apiVersion: v1 kind: Service metadata: labels: name: app1 name: app1 namespace: default spec: type: NodePort ports: - <strong>port: 8080 t…