惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

MongoDB | Blog
MongoDB | Blog
V
V2EX
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
有赞技术团队
有赞技术团队
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
罗磊的独立博客
月光博客
月光博客
爱范儿
爱范儿
D
Docker
U
Unit 42
P
Proofpoint News Feed
I
InfoQ
腾讯CDC
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
L
LangChain Blog
V
Visual Studio Blog
IT之家
IT之家
Vercel News
Vercel News
G
Google Developers Blog
M
MIT News - Artificial intelligence
美团技术团队
The GitHub Blog
The GitHub Blog
阮一峰的网络日志
阮一峰的网络日志
MyScale Blog
MyScale Blog

OhYee 博客

小鹏辅助驾驶测评|OhYee 博客 小鹏非支持手机开启自动解锁|OhYee 博客 使用函数计算实现 301 重定向|OhYee 博客 针对 HTML 内容使用 Ant Design 图片弹框|OhYee 博客 博客进程泄露及僵尸进程解决|OhYee 博客 蓝易云服务器体验|OhYee 博客 SSH 调起本地 VSCode|OhYee 博客 【2022 秋招内推】阿里云后端研发工程师|OhYee 博客 使用函数计算获取 IP 地址信息|OhYee 博客 正确获取客户端 IP/HTTP Header 也可能重复|OhYee 博客 评测 Oculus Quest2 及 BigScreen|OhYee 博客 NextJS 热重载保留状态|OhYee 博客 如何优雅地贴 gist 代码|OhYee 博客 Linux 精细化文件权限|OhYee 博客 VSCode 容器开发环境|OhYee 博客 Clash 的不兼容更新排查|OhYee 博客 Zeek 导出 PCAP|OhYee 博客 记一次 ssh 配置问题|OhYee 博客 Git Commit 规范化工具|OhYee 博客 谈谈《星之卡比-探索发现》|OhYee 博客 VSCode 快捷键绑定 Shell 命令|OhYee 博客 ASN.1 语法及 X.509 证书格式解析解析|OhYee 博客 腾讯企业邮箱忽略 MX 记录发信|OhYee 博客 Chrome/Edge 标签组插件|OhYee 博客 【应届内推】阿里云后端研发工程师|OhYee 博客 损坏的 Typecho 备份处理为 JSON|OhYee 博客 VS Code VIM 插件高效使用|OhYee 博客 SSH 正反向代理|OhYee 博客 Let's Encrypt 根证书过期引发的问题|OhYee 博客 OpenWRT 忽略内核依赖|OhYee 博客
LeetCode 135.分发糖果|OhYee 博客
2020-12-24 · via OhYee 博客

LeetCode 135.分发糖果

题目描述

老师想给孩子们分发糖果,有 NN 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。

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

每个孩子至少分配到 11 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?

示例1

输入: [1,0,2]
输出: 5
解释:

你可以分别给这三个孩子分发 2、1、2 颗糖果。

示例2

输入: [1,2,2]
输出: 4
解释:

你可以分别给这三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这已满足上述两个条件

题解

题目代码很简单,分别从左往右和从右往左扫描一次,取每个人的最大值相加。重点在于 为什么这么做是对的?

根据贪心的思路,应该确保尽可能每一个人少分糖果,如果符合条件就只给 11 个。而这里限制糖果个数的因素为:相邻的孩子中,评分高的孩子必须获得更多的糖果
也即,每个孩子最终的糖果只依赖于其左右相邻的孩子,与更远的孩子无关。

这部分思路如果按照动态规划,可以理解为:如果孩子比上一个孩子评分高,那么他获得的糖果也应该更高。(逻辑上的上一个,与实际的顺序无关。按照题目要求,为相邻的两个孩子)
因此,实际上状态转移方程为

dp[i]=max({1,score[i−1]≤score[i]dp[i−1]+1,score[i−1]>score[i],{1,score[i−]≤score[i]dp[i+1]+1,score[i+1]>score[i])dp[i] = max(\begin{cases} 1,&score[i-1] \le score[i]\\dp[i-1]+1,&score[i-1] \gt score[i] \end{cases}, \begin{cases} 1,&score[i-] \le score[i]\\dp[i+1]+1,&score[i+1] \gt score[i] \end{cases})

接下来要考虑的就是转移方程的独立性:为什么可以从两头计算?
上面的转移方程存在一个问题,就是在计算 dp[i]dp[i] 时,需要预先拥有 dp[i−1]dp[i-1]dp[i+1]dp[i+1],但这两个数值的计算又依赖于 dp[i]dp[i] 本身。因此常规的动态规划思路无法解决。

但是,如果再次观察这个方程,max 中的两个公式本身是不相关的,也即可以将其分成 dp[i]={1,score[i−1]≤score[i]dp[i−1]+1,score[i−1]>score[i]dp[i] = \begin{cases} 1,&score[i-1] \le score[i]\\dp[i-1]+1,&score[i-1] \gt score[i] \end{cases}dp[i]={1,score[i−]≤score[i]dp[i+1]+1,score[i+1]>score[i]dp[i] = \begin{cases} 1,&score[i-] \le score[i]\\dp[i+1]+1,&score[i+1] \gt score[i] \end{cases} 两部分。
而这两部分是可以通过状态转移方程按顺序求解的,最后求每个位置的最大值即可。

原始的状态转移方程很容易写出,但是后续的化简可能会较难发现(但是如果真的把这个状态转移方程写出来,并且因为次序问题而觉得动态规划不能解决问题时,还是很可能发现化简方案的)
因此,对于这种问题,写出状态转移方程至关重要,即使状态转移方程可能无法直接求解

代码

class Solution:
    def candy(self, ratings: List[int]) -> int:
        n = len(ratings)
        left = [0] * n
        right = [0] * n
        res = 0
        for i in range(n):
            j = n-i-1
            left[i]  = left[i-1]  + 1 if i != 0   and ratings[i] > ratings[i-1] else 1
            right[j] = right[j+1] + 1 if j != n-1 and ratings[j] > ratings[j+1] else 1    
        for i in range(n):
            res += max(left[i], right[i])
        return res