普通视图
React使用useLayoutEffect解决操作DOM页面闪烁问题
HelloGitHub 第 117 期
科技爱好者周刊(第 379 期):《硅谷钢铁侠》摘录
这里记录每周值得分享的科技内容,周五发布。([通知] 下周元旦假期,周刊休息。)
本杂志开源,欢迎投稿。另有《谁在招人》服务,发布程序员招聘信息。合作请邮件联系(yifeng.ruan@gmail.com)。
封面图
![]()
哈尔滨19米大雪人,完工之前的样子。(via cgtn@instagram)
《硅谷钢铁侠》摘录
最近,我读了一本十年前的马斯克传记《硅谷钢铁侠》(中信出版社,2016)。
![]()
按理说,这本书已经过时了,这十年马斯克发生太多事情了。
我是睡觉前随手拿起来,翻了几页,看得津津有味,就读完了。
这本是马斯克的授权传记,他本人亲自接受了采访,还挺有料的。而且,因为我已经知道后续的发展,所以读到十年前的采访,反而有更多启发。
他的人生确实传奇,白手起家,家里给的最大帮助就是从南非移民到加拿大,后面都是自己奋斗出来的。
他创立了 Paypal,然后把卖掉它的钱拿来又创办了三家公司:特斯拉、SpaceX 和 SolarCity。
这太疯狂了,他一个外行同时进入了三个不同的行业----电动汽车、宇宙航天和太阳能----这些行业都刚萌芽,没有任何个人创业成功的先例。
更疯狂的是,他居然把这三家公司都做成了,而且都做到了世界第一(SolarCity 后并入特斯拉),他也因此变成了世界首富,你说神奇不神奇。
读完全书,我的最大感受是,还是要动手做事,没准真能做成。想他人不敢想,做他人不敢做。即使最狂野的梦想,只要全心投入,用力去做,也是有可能成功的。
下面就是我的一点摘录。
(1)
特斯拉最艰难的时候,非常接近于破产倒闭。
马斯克对外宣传,特斯拉是一家汽车公司,但实际上,他们只是一群年轻人租了一间大厂房,更像是在捣鼓汽车的大型实验室。
(2)
马斯克非常不理解,为什么有人设计了车灯开关。
他说:"真是多此一举。天黑时车灯自动打开,就这么简单。"
(3)
特斯拉的第一版设计稿,因为设计师没想好门把手的形状,就没画上去。
没想到马斯克很喜欢这个没有门把手的车型,就决定门把手应该在有需要的时候自动弹出。
![]()
(4)
马斯克认为,未来会有人口危机,主张多生孩子。
他认真考虑了,怎么在特斯拉后排安装婴儿座椅。传统的车门设计,使得把婴儿座椅和小孩安置在后排非常不方便,所以特斯特的车门设计采用了"鹰翼门"。
![]()
(5)
特斯拉的第一款车型是跑车,但没有大量生产。真正大量生产的第一款车型是 Model S,最初的名字是 Model Sedan。
Sedan 这个词的意思就是轿车,用来跟跑车相区别。但是马斯克认为这个词太平淡了。英国人习惯称轿车为 Saloon,这听上一样不伦不类。最后,就索性只保留第一个字母,称为 Model S。
(6)
马斯克对员工的要求是,全情投入你的工作,并把事情搞定。
不要等待上级的指导和详细指示,也不要等待别人的反馈意见,你要主动想办法把工作完成。
(7)
他认为,一个人独立工作,是最佳的工作状态。
一个人不需要开会、不需要与谁达成共识,也不需要在项目中帮助其他人。你一个人就可以持续地工作、工作、再工作。
(8)
特斯拉员工最害怕的事情,就是向马斯克申请额外的时间或者经费。
你一定要事先做好详细准备,跟他解释为什么必须招更多的人,以及需要追加的时间和资金预算。如果有招聘目标,还要准备那个人的简历。
(9)
如果你一上来就告诉马斯克,某件事情做不了,他会马上把你轰出办公室,甚至可能当场解雇你。
在马斯克看来,某件事办不成的唯一原因,就是违背了基本的物理原理。但是即使这样,你也必须做足了功课,深入每一个技术环节,向他解释为什么行不通。
(10)
马斯克要求员工,项目没完成之前,周六和周日依然要努力工作,并睡在桌子底下。
有些人反对,表示员工也需要休息,有时间陪陪家人。
马斯克说:"我们破产之后,你们会有大量时间陪家人。"
(11)
马斯克有自己计算时间价值的方法。他预期10年后,公司的日营收可以达到1000万美元,所以进度每拖延一天,就相当于多损失1000万美元。
(12)
马斯克的根本想法是改变这个世界,他总是喜欢谈论人类的生存问题。
早在他开始创业的时候,就已经得出了结论,那就是生命是短暂的。如果你真的意识到这一点,你就会知道,活着的时候工作越努力越好。
科技动态
1、黑色圣诞卡
爱沙尼亚交通警察向800多名危险驾驶者,寄送了黑色圣诞卡,提醒他们新的一年必须安全驾驶。
![]()
这些人都是过去违反交通规则的司机,最常见的问题是超速和不系安全带。
圣诞卡上是一起交通事故的现场,黑漆漆的深夜,天空中有明亮的月亮,公路上有交通事故后的车辆残骸,远处还有车灯的亮光。
![]()
一个有趣的统计是,虽然人们常说女司机是"马路杀手",但是这800多个危险驾驶者里面,只有33名女性。
世界最大 CDN 服务商 Cloudflare,发布了《2025全球互联网报告》,公布了它的统计数据。
![]()
2025年,全球互联网流量上升19%,由于网民数量基本没变,所以多出来的流量来自 AI 爬虫。
流量最大的前10大互联网服务:谷歌、脸书、苹果......
![]()
移动流量中,苹果设备占35%,安卓设备占65%。
浏览器排行是,Chrome 66%,Safari 15.4%,Edge 7.4%。
![]()
3、违停巡逻车
上海警方启用无人驾驶的违章停车巡逻车。
![]()
这辆小车自动在马路上巡逻,对路面进行抓拍。
一旦发现违停车辆,它就会识别车牌,将其上传警务系统,系统后台会发送提醒短信给车主,要求在12分钟内驶离。
12分钟后,小车就会返回点位进行检查,将相关信息回传后台,并经民警审核后开罚单。
![]()
据报道,12月18日一天,它共发现违停车辆119辆次。
4、室内过山车
一家瑞典的创意工作室,在他们的办公室建造了世界唯一的室内过山车。
![]()
这个过山车途径办公室的各个角落,总长60米,最高的地方距离地面有3米。
![]()
坐上这个过山车,你就能游览一圈办公室,看到同事们在干什么。
![]()
工作室负责人说,建造它的目的是"促进员工之间的互动,以及打破常规,培养创造力。"
![]()
文章
1、分布式架构的演化(英文)
![]()
本文将分布式架构分成三种:P2P、联邦式(比如 Mastodon)、中继式(比如 Nostr)。作者认为,对于大型分布式应用,中继式架构才是未来方向。
2、什么是 GitHub 自托管 Runner?(中文)
![]()
GitHub Actions 有一个 self-hosted runner 功能,让 action 运行在你自己的服务器。本文详细介绍它的概念、原理,并结合案例进行实践。(@luhuadong 投稿)
3、CSS Grid Lanes 布局(英文)
![]()
浏览器开始支持 CSS 的 Grid Lanes 布局了,大大方便了瀑布流的实现。
4、6502 指令集适用汇编语言初学者(英文)
![]()
6502 是一块诞生于1975年的 CPU,很多早期电脑(比如 Apple II)都使用它。作者解释,为什么你应该用它,作为学习汇编语言的第一个指令集。
5、你应该多用/tmp目录(英文)
![]()
作者提出,Linux 系统的/tmp目录用起来很方便,完全可以把它当作自己的临时性目录。
6、中国的清洁能源战略(英文)
![]()
《纽约时报》驻华记者的长文,体验当代中国的生活,比如无人驾驶、无人机送餐,他说"感觉像生活在未来"。
工具
1、MADOLA
![]()
一种新的数学脚本语言,像编程一样写数学公式,可以编译成 HTML 格式作为文档,也可以编译成 C++ 或 WebAssembly 直接运行。(@AI4Engr 投稿)
2、CattoPic
![]()
一个基于 Cloudflare Worker 的图片托管服务,将图片上传到 Cloudflare 进行推过,支持自动格式转换、标签管理。(@Yuri-NagaSaki 投稿)
3、termdev
直接在终端,通过连接 Chrome Devtool 调试网页。(@taotao7 投稿)
![]()
为 Rust 语言的命令行项目添加一个横幅图案。(@coolbeevip 投稿)
![]()
macOS 菜单栏的资源监控工具,监控 CPU、内存、磁盘、网络和进程活动。(@nobbbbby 投稿)
![]()
C/C++ 代码的静态检查工具,可以接入 CI/CD 流程,简化代码质量管理。(@shenxianpeng 投稿)
7、Rote
![]()
开源的 Web 笔记软件,需要自己架设。(@Rabithua 投稿)
![]()
JS 的数据可视化框架,用于在网页生成各种信息图,内置200多种模板。(@Aarebecca 投稿)
![]()
天气时钟看板,适合老旧的电子设备再利用。(@teojs 投稿)
10、离线版问卷
![]()
开源 Web 应用,用来设计和托管调查问卷/报名表。(@chenbz777 投稿)
11、Xget
![]()
基于边缘计算(如 Cloudflare Workers/Vercel/Netlify)的加速引擎,可以加速程序员网站的访问速度,比如将github.com域名替换成xget.xi-xu.me/gh。(@xixu-me 投稿)
12、BoxLite
![]()
一个 Python 库,可以在脚本中运行一个微型虚拟机,提供硬件隔离。(@DorianZheng 投稿)
13、Green Wall
![]()
生成你的 GitHub 年度报告。(@Codennnn 投稿)
![]()
面向出海项目的 Next.js + Cloudflare 全栈项目模板,集成 Edge Runtime、D1 数据库、R2 存储。(@TangSY 投稿)
AI 相关
1、Chaterm
![]()
带有 AI 功能的智能终端工具,可以用自然语言完成命令行操作。(@zhouyu123666 投稿)
2、miniCC
![]()
网友开发的 AI 编程工具 Claude Code 替代品,主要用于学习目的。(@Disdjj 投稿)
![]()
一个开源的纯前端应用,通过 AI 翻译安卓资源文件,支持多语言同步、差异校验。(@huanfeng 投稿)
4、octopus
![]()
个人用户的大模型 API 聚合工具,支持接入多个模型供应商,提供负载均衡、分组名称、使用量统计等功能。(@bestruirui 投稿)
5、Vexor
![]()
一个 Python 工具,对当前目录的文件进行向量嵌入,用来语义搜索。(@scarletkc 投稿)
6、Tada
![]()
开源的任务管理应用,带有 AI 总结功能。(@Leaomato 投稿)
资源
1、大模型原理(英文)
![]()
一篇相对好懂的大模型原理解释,文章不长,并且还有大量的互动图形,写得非常好,推荐阅读。
2、编程语言速度比较
![]()
这个网站使用不同的计算机语言,通过莱布尼茨公式计算 π 值,然后给出运行速度的排名,最快是 C++(clang++),最慢是 Python (CPython)。
![]()
这个网页提供三个 ZIP 炸弹文件的下载,其中最小一个只有 42KB,但是解压后的大小是 5.5GB。
图片
《自然》杂志评选的一组2025年最佳科学图片。
两只争夺领地的青蛙。
![]()
南非废弃天文台长出的蘑菇。
![]()
2、帽子,乌龟和幽灵
2022年,一个业余数学家 David Smith 发现了一个有点像帽子的奇特形状。
![]()
这个形状的奇特之处在于,它可以无限不重复地铺满整个空间,且不形成周期性的重复图案。
![]()
不久后,他又发现了两种稍加变化的形状,称为乌龟和幽灵,也可以不重复地平铺平面。
![]()
![]()
下面就是这三种形状各自平铺的图案。
![]()
言论
1、
我使用氛围编程会感到疲惫,AI 生成代码的速度太快了,我的大脑跟不上,无法及时完成代码验收或审查。我必须休息一段时间,才能重新开始。
-- 《氛围编程疲劳》
2、
制造汽车是非常困难的一件事。一辆车大约有3万个独立零部件,公司可能只会采购3000个,因为像车头灯这样的部件,是作为一个整体采购的,但它实际上包含很多组件。
里面的二级、三级、四级供应商提供的零部件,任何一个出现问题都可能导致整车的问题。
3、
数码世界的现状是,很多人(尤其是大多数老年人)已经放弃了抵抗,任由电子设备将他们带到任何地方。
因为一旦你想搞清楚电子设备的运作,就会发现,在便利的幌子下,一切都充满了敌意,暗箱操作无处不在,不可能完全理清。你想从它们手中夺回个人数据和隐私会非常艰苦,而且注定失败,最终只会带来更大的挫败感。
-- 《一切并非必然》
4、
现在的学生拥有前所未有的优质教育资源,但他们却陷入成千上万种选择中不知该学什么、该用什么资源的困境。拥有资源并不意味着就能找到方向。
-- 《不要关闭你的大脑》
5、
危险并非来自中国的崛起,而是美国的思维模式。如果把科学视为零和博弈,那么每一项中国专利看起来都像是美国的损失。但创意是非竞争性的:中国的科研突破不会让美国人变穷,而是会让世界变得更富有。多极化的科学世界意味着更快的增长、更大的财富和加速的技术进步。
-- 《中国的创新》
往年回顾
西蒙·威利森的年终总结,梁文锋的访谈(#332)
电动皮卡 Cybertruck 的 48V 供电(#282)
好用的平面设计软件(#232)
新人优惠的风险(#182)
(完)
文档信息
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
- 发表日期: 2025年12月26日
每日一题-商店的最少代价🟡
给你一个顾客访问商店的日志,用一个下标从 0 开始且只包含字符 'N' 和 'Y' 的字符串 customers 表示:
- 如果第
i个字符是'Y',它表示第i小时有顾客到达。 - 如果第
i个字符是'N',它表示第i小时没有顾客到达。
如果商店在第 j 小时关门(0 <= j <= n),代价按如下方式计算:
- 在开门期间,如果某一个小时没有顾客到达,代价增加
1。 - 在关门期间,如果某一个小时有顾客到达,代价增加
1。
请你返回在确保代价 最小 的前提下,商店的 最早 关门时间。
注意,商店在第 j 小时关门表示在第 j 小时以及之后商店处于关门状态。
示例 1:
输入:customers = "YYNY" 输出:2 解释: - 第 0 小时关门,总共 1+1+0+1 = 3 代价。 - 第 1 小时关门,总共 0+1+0+1 = 2 代价。 - 第 2 小时关门,总共 0+0+0+1 = 1 代价。 - 第 3 小时关门,总共 0+0+1+1 = 2 代价。 - 第 4 小时关门,总共 0+0+1+0 = 1 代价。 在第 2 或第 4 小时关门代价都最小。由于第 2 小时更早,所以最优关门时间是 2 。
示例 2:
输入:customers = "NNNNN" 输出:0 解释:最优关门时间是 0 ,因为自始至终没有顾客到达。
示例 3:
输入:customers = "YYYY" 输出:4 解释:最优关门时间是 4 ,因为每一小时均有顾客到达。
提示:
1 <= customers.length <= 105-
customers只包含字符'Y'和'N'。
[Java] 前缀和 & 空间优化
解法一:前缀和
利用前缀和,后缀和统计N,Y的数字即可,最后计算出最小的答案。
- 时间复杂度:$O(n)$
- 空间复杂度:$O(n)$
class Solution {
public int bestClosingTime(String cus) {
int n = cus.length(), maxv = 100005, ans = 0;
char[] arr = cus.toCharArray();
int[] left = new int[n + 5], right = new int[n + 5];
for (int i = 1; i <= n; i++) left[i] += left[i - 1] + (arr[i - 1] == 'N' ? 1 : 0);
for (int i = n; i >= 1; i--) right[i] += right[i + 1] + (arr[i - 1] == 'Y' ? 1 : 0);
for (int i = 1; i <= n + 1; i++) {
if (left[i - 1] + right[i] < maxv) {
maxv = left[i - 1] + right[i];
ans = i - 1;
}
}
return ans;
}
}
解法二:空间优化
先统计所有Y的个数,然后对于当前字符计算代价即可,最后更新总代价。
class Solution {
public int bestClosingTime(String cus) {
int n = cus.length(), maxv = 100005, ans = 0;
char[] arr = cus.toCharArray();
int left_right = 0;
for (int i = n; i >= 1; i--) left_right += (arr[i - 1] == 'Y' ? 1 : 0);
for (int i = 1; i <= n + 1; i++) {
if (left_right < maxv) {
maxv = left_right;
ans = i - 1;
}
if (i <= n && arr[i - 1] == 'Y') left_right--;
else left_right++;
}
return ans;
}
}
- 时间复杂度:$O(n)$
- 空间复杂度:$O(1)$
如果有问题,欢迎评论区交流, 如果有帮助到你,请给题解点个赞和收藏哈~~~
前后缀分解,一次遍历优化(Python/Java/C++/Go)
题意
把 $\textit{customers}$ 分成两段,第一段计算 $\texttt{N}$ 的个数 $\textit{preN}$,第二段计算 $\texttt{Y}$ 的个数 $\textit{sufY}$。
计算 $\textit{preN}+\textit{sufY}$ 的最小值对应的最小分割位置 $i$,其中 $[0,i-1]$ 是第一段,$[i,n-1]$ 是第二段。
注:也可以不分割,相当于其中一段是空的。
思路
先假设所有字母都在第二段,统计 $\textit{customers}$ 中 $\texttt{Y}$ 的个数 $\textit{sufY}$。
想象一根分割线在从左到右移动,$\textit{customers}[i]$ 原来在第二段,现在在第一段。如果 $\textit{customers}[i] = \texttt{N}$,那么把 $\textit{preN}$ 加一($\textit{preN}$ 初始值为 $0$),否则把 $\textit{sufY}$ 减一。
答案为 $\textit{preN}+\textit{sufY}$ 最小时对应的分割位置。
代码实现时,$\textit{preN}+\textit{sufY}$ 可以合并为一个变量 $\textit{penalty}$。
优化前
class Solution:
def bestClosingTime(self, customers: str) -> int:
min_penalty = penalty = customers.count('Y')
ans = 0 # [0,n-1] 是第二段
for i, c in enumerate(customers):
penalty += 1 if c == 'N' else -1
if penalty < min_penalty:
min_penalty = penalty
ans = i + 1 # [0,i] 是第一段,[i+1,n-1] 是第二段
return ans
class Solution {
public int bestClosingTime(String customers) {
char[] s = customers.toCharArray();
int penalty = 0;
for (char c : s) {
if (c == 'Y') {
penalty++;
}
}
int minPenalty = penalty;
int ans = 0; // [0,n-1] 是第二段
for (int i = 0; i < s.length; i++) {
penalty += s[i] == 'N' ? 1 : -1;
if (penalty < minPenalty) {
minPenalty = penalty;
ans = i + 1; // [0,i] 是第一段,[i+1,n-1] 是第二段
}
}
return ans;
}
}
class Solution {
public:
int bestClosingTime(string customers) {
int penalty = ranges::count(customers, 'Y');
int min_penalty = penalty;
int ans = 0; // [0,n-1] 是第二段
for (int i = 0; i < customers.size(); i++) {
penalty += customers[i] == 'N' ? 1 : -1;
if (penalty < min_penalty) {
min_penalty = penalty;
ans = i + 1; // [0,i] 是第一段,[i+1,n-1] 是第二段
}
}
return ans;
}
};
func bestClosingTime(customers string) int {
penalty := strings.Count(customers, "Y")
minPenalty := penalty
ans := 0 // [0,n-1] 是第二段
for i, c := range customers {
if c == 'N' {
penalty++
} else {
penalty--
}
if penalty < minPenalty {
minPenalty = penalty
ans = i + 1 // [0,i] 是第一段,[i+1,n-1] 是第二段
}
}
return ans
}
优化:一次遍历
第一次遍历(统计 $\texttt{Y}$ 的个数)是多余的。
打个比方,绝对温度下冰点为 $273,\mathrm{K}$,摄氏温度下冰点为 $0~^\circ\mathrm{C}$。如果只关心哪一天最冷,用哪个单位计算都可以。
或者说,把折线图往下平移(第一个点移到坐标零点),最低点的横坐标仍然不变。
class Solution:
def bestClosingTime(self, customers: str) -> int:
min_penalty = penalty = ans = 0
for i, c in enumerate(customers):
penalty += 1 if c == 'N' else -1
if penalty < min_penalty:
min_penalty = penalty
ans = i + 1
return ans
class Solution {
public int bestClosingTime(String customers) {
int penalty = 0;
int minPenalty = 0;
int ans = 0;
for (int i = 0; i < customers.length(); i++) {
penalty += customers.charAt(i) == 'N' ? 1 : -1;
if (penalty < minPenalty) {
minPenalty = penalty;
ans = i + 1;
}
}
return ans;
}
}
class Solution {
public:
int bestClosingTime(string customers) {
int penalty = 0, min_penalty = 0, ans = 0;
for (int i = 0; i < customers.size(); i++) {
penalty += customers[i] == 'N' ? 1 : -1;
if (penalty < min_penalty) {
min_penalty = penalty;
ans = i + 1;
}
}
return ans;
}
};
func bestClosingTime(customers string) (ans int) {
penalty, minPenalty := 0, 0
for i, c := range customers {
if c == 'N' {
penalty++
} else {
penalty--
}
if penalty < minPenalty {
minPenalty = penalty
ans = i + 1
}
}
return
}
复杂度分析
- 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 是 $\textit{customers}$ 的长度。
- 空间复杂度:$\mathcal{O}(1)$。
相似题目
分类题单
- 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
- 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
- 单调栈(基础/矩形面积/贡献法/最小字典序)
- 网格图(DFS/BFS/综合应用)
- 位运算(基础/性质/拆位/试填/恒等式/思维)
- 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
- 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
- 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
- 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
- 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
- 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
- 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)
欢迎关注 B站@灵茶山艾府
枚举 & 前缀和
解法:枚举 & 前缀和
为了方便计算前(后)缀和,我们认为 customers 的下标是从 $1$ 开始的,最后答案减 $1$ 即可。
维护 $f(i)$ 表示第 $1$ 到第 $i$ 个字符有几个 N(初值 $f(0) = 0$),$g(i)$ 表示第 $i$ 到第 $n$ 个字符有几个 Y(初值 $g(n + 1) = 0$),那么第 $h$ 小时关店的代价即为 $f(h - 1) + g(h)$。
从 $1$ 到 $(n + 1)$ 枚举所有 $h$ 并选出代价最小的即可。复杂度 $\mathcal{O}(n)$。
参考代码(c++)
###c++
class Solution {
public:
int bestClosingTime(string customers) {
int n = customers.size();
int f[n + 2], g[n + 2];
// 计算前缀有几个 N
f[0] = 0;
for (int i = 1; i <= n; i++) f[i] = f[i - 1] + (customers[i - 1] == 'N' ? 1 : 0);
// 计算后缀有几个 Y
g[n + 1] = 0;
for (int i = n; i > 0; i--) g[i] = g[i + 1] + (customers[i - 1] == 'Y' ? 1 : 0);
// 枚举答案
int ans = 0, best = 1e9;
for (int i = 1; i <= n + 1; i++) {
int tmp = f[i - 1] + g[i];
if (tmp < best) ans = i, best = tmp;
}
return ans - 1;
}
};
React Hooks 的“天条”:为啥绝对不能写在 if 语句里?
鸿蒙ArkUI如何使用RGB、十六进制等设置颜色值?
C# 正则表达式(3):分组与捕获——从子串提取到命名分组
从一则内存快照看iframe泄漏:活跃与Detached状态的回收差异
【双解】暴力 -> 贪心+排序/堆,解释贪心的正确性
[Python3/Java/C++/Go/TypeScript] 一题一解:贪心 + 排序(清晰题解)
方法一:贪心 + 排序
为了使得幸福值之和尽可能大,我们应该优先选择幸福值大的孩子。因此,我们可以对孩子按照幸福值从大到小排序,然后依次选择 $k$ 个孩子。对于当前第 $i$ 个孩子,能够得到的幸福值为 $\max(\textit{happiness}[i] - i, 0)$,最后返回这 $k$ 个孩子的幸福值之和。
###python
class Solution:
def maximumHappinessSum(self, happiness: List[int], k: int) -> int:
happiness.sort(reverse=True)
ans = 0
for i, x in enumerate(happiness[:k]):
x -= i
ans += max(x, 0)
return ans
###java
class Solution {
public long maximumHappinessSum(int[] happiness, int k) {
Arrays.sort(happiness);
long ans = 0;
for (int i = 0, n = happiness.length; i < k; ++i) {
int x = happiness[n - i - 1] - i;
ans += Math.max(x, 0);
}
return ans;
}
}
###cpp
class Solution {
public:
long long maximumHappinessSum(vector<int>& happiness, int k) {
sort(happiness.rbegin(), happiness.rend());
long long ans = 0;
for (int i = 0, n = happiness.size(); i < k; ++i) {
int x = happiness[i] - i;
ans += max(x, 0);
}
return ans;
}
};
###go
func maximumHappinessSum(happiness []int, k int) (ans int64) {
sort.Ints(happiness)
for i := 0; i < k; i++ {
x := happiness[len(happiness)-i-1] - i
ans += int64(max(x, 0))
}
return
}
###ts
function maximumHappinessSum(happiness: number[], k: number): number {
happiness.sort((a, b) => b - a);
let ans = 0;
for (let i = 0; i < k; ++i) {
const x = happiness[i] - i;
ans += Math.max(x, 0);
}
return ans;
}
###rust
impl Solution {
pub fn maximum_happiness_sum(mut happiness: Vec<i32>, k: i32) -> i64 {
happiness.sort_unstable_by(|a, b| b.cmp(a));
let mut ans: i64 = 0;
for i in 0..(k as usize) {
let x = happiness[i] as i64 - i as i64;
if x > 0 {
ans += x;
}
}
ans
}
}
###cs
public class Solution {
public long MaximumHappinessSum(int[] happiness, int k) {
Array.Sort(happiness, (a, b) => b.CompareTo(a));
long ans = 0;
for (int i = 0; i < k; i++) {
int x = happiness[i] - i;
if (x <= 0) {
break;
}
ans += x;
}
return ans;
}
}
时间复杂度 $O(n \times \log n + k)$,空间复杂度 $O(\log n)$。其中 $n$ 是数组 $\textit{happiness}$ 的长度。
有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈 😄~