普通视图

发现新文章,点击刷新页面。
今天 — 2025年12月26日技术

HelloGitHub 第 117 期

2025年12月26日 08:03
本期共有 39 个项目,包含 C 项目 (2),C# 项目 (3),C++ 项目 (1),Go 项目 (4),Java 项目 (2),JavaScript 项目 (5),Kotlin 项目 (2),PHP 项目 (1),Python 项目 (5),Rust 项目 (2),Swift 项目 (1),人工智能 (5),其它 (6)

科技爱好者周刊(第 379 期):《硅谷钢铁侠》摘录

作者 阮一峰
2025年12月26日 07:51

这里记录每周值得分享的科技内容,周五发布。([通知] 下周元旦假期,周刊休息。

本杂志开源,欢迎投稿。另有《谁在招人》服务,发布程序员招聘信息。合作请邮件联系(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名女性。

2、2025全球互联网报告

世界最大 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 投稿)

4、tui-banner

为 Rust 语言的命令行项目添加一个横幅图案。(@coolbeevip 投稿)

5、Alertivity

macOS 菜单栏的资源监控工具,监控 CPU、内存、磁盘、网络和进程活动。(@nobbbbby 投稿)

6、cpp‑linter

C/C++ 代码的静态检查工具,可以接入 CI/CD 流程,简化代码质量管理。(@shenxianpeng 投稿)

7、Rote

开源的 Web 笔记软件,需要自己架设。(@Rabithua 投稿)

8、Infographic

JS 的数据可视化框架,用于在网页生成各种信息图,内置200多种模板。(@Aarebecca 投稿)

9、Clock Dashboard

天气时钟看板,适合老旧的电子设备再利用。(@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 投稿)

14、edge-next-starter

面向出海项目的 Next.js + Cloudflare 全栈项目模板,集成 Edge Runtime、D1 数据库、R2 存储。(@TangSY 投稿)

AI 相关

1、Chaterm

带有 AI 功能的智能终端工具,可以用自然语言完成命令行操作。(@zhouyu123666 投稿)

2、miniCC

网友开发的 AI 编程工具 Claude Code 替代品,主要用于学习目的。(@Disdjj 投稿)

3、Android Trans Tool Plus

一个开源的纯前端应用,通过 AI 翻译安卓资源文件,支持多语言同步、差异校验。(@huanfeng 投稿)

4、octopus

个人用户的大模型 API 聚合工具,支持接入多个模型供应商,提供负载均衡、分组名称、使用量统计等功能。(@bestruirui 投稿)

5、Vexor

一个 Python 工具,对当前目录的文件进行向量嵌入,用来语义搜索。(@scarletkc 投稿)

6、Tada

开源的任务管理应用,带有 AI 总结功能。(@Leaomato 投稿)

资源

1、大模型原理(英文)

一篇相对好懂的大模型原理解释,文章不长,并且还有大量的互动图形,写得非常好,推荐阅读。

2、编程语言速度比较

这个网站使用不同的计算机语言,通过莱布尼茨公式计算 π 值,然后给出运行速度的排名,最快是 C++(clang++),最慢是 Python (CPython)。

3、更好的 ZIP 炸弹

这个网页提供三个 ZIP 炸弹文件的下载,其中最小一个只有 42KB,但是解压后的大小是 5.5GB。

图片

1、2025年最佳科学图片

《自然》杂志评选的一组2025年最佳科学图片。

两只争夺领地的青蛙。

南非废弃天文台长出的蘑菇。

2、帽子,乌龟和幽灵

2022年,一个业余数学家 David Smith 发现了一个有点像帽子的奇特形状。

这个形状的奇特之处在于,它可以无限不重复地铺满整个空间,且不形成周期性的重复图案。

不久后,他又发现了两种稍加变化的形状,称为乌龟和幽灵,也可以不重复地平铺平面。

下面就是这三种形状各自平铺的图案。

言论

1、

我使用氛围编程会感到疲惫,AI 生成代码的速度太快了,我的大脑跟不上,无法及时完成代码验收或审查。我必须休息一段时间,才能重新开始。

-- 《氛围编程疲劳》

2、

制造汽车是非常困难的一件事。一辆车大约有3万个独立零部件,公司可能只会采购3000个,因为像车头灯这样的部件,是作为一个整体采购的,但它实际上包含很多组件。

里面的二级、三级、四级供应商提供的零部件,任何一个出现问题都可能导致整车的问题。

-- 汽车创业公司 Rivian 的 CEO 专访

3、

数码世界的现状是,很多人(尤其是大多数老年人)已经放弃了抵抗,任由电子设备将他们带到任何地方。

因为一旦你想搞清楚电子设备的运作,就会发现,在便利的幌子下,一切都充满了敌意,暗箱操作无处不在,不可能完全理清。你想从它们手中夺回个人数据和隐私会非常艰苦,而且注定失败,最终只会带来更大的挫败感。

-- 《一切并非必然》

4、

现在的学生拥有前所未有的优质教育资源,但他们却陷入成千上万种选择中不知该学什么、该用什么资源的困境。拥有资源并不意味着就能找到方向。

-- 《不要关闭你的大脑》

5、

危险并非来自中国的崛起,而是美国的思维模式。如果把科学视为零和博弈,那么每一项中国专利看起来都像是美国的损失。但创意是非竞争性的:中国的科研突破不会让美国人变穷,而是会让世界变得更富有。多极化的科学世界意味着更快的增长、更大的财富和加速的技术进步。

-- 《中国的创新》

往年回顾

西蒙·威利森的年终总结,梁文锋的访谈(#332)

电动皮卡 Cybertruck 的 48V 供电(#282)

好用的平面设计软件(#232)

新人优惠的风险(#182)

(完)

文档信息

  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证
  • 发表日期: 2025年12月26日

每日一题-商店的最少代价🟡

2025年12月26日 00:00

给你一个顾客访问商店的日志,用一个下标从 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] 前缀和 & 空间优化

作者 Tizzi
2022年11月29日 11:18

解法一:前缀和

利用前缀和,后缀和统计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)

作者 endlesscheng
2022年11月27日 09:02

题意

把 $\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)$。

相似题目

3694. 删除子字符串后不同的终点

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

枚举 & 前缀和

作者 tsreaper
2022年11月27日 00:09

解法:枚举 & 前缀和

为了方便计算前(后)缀和,我们认为 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;
    }
};
昨天 — 2025年12月25日技术

[Python3/Java/C++/Go/TypeScript] 一题一解:贪心 + 排序(清晰题解)

作者 lcbin
2025年12月25日 07:33

方法一:贪心 + 排序

为了使得幸福值之和尽可能大,我们应该优先选择幸福值大的孩子。因此,我们可以对孩子按照幸福值从大到小排序,然后依次选择 $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}$ 的长度。


有任何问题,欢迎评论区交流,欢迎评论区提供其它解题思路(代码),也可以点个赞支持一下作者哈 😄~

🎉TinyVue v3.27.0 正式发布:增加 Space 新组件,ColorPicker 组件支持线性渐变

2025年12月25日 17:22
你好,我是 Kagol,个人公众号:前端开源星球。 我们非常高兴地宣布 TinyVue v3.27.0 正式发布🎉。该版本聚焦于增强可定制性与可访问性,新增若干实用组件能力,并修复了大量用户反馈的关键
❌
❌