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

推荐订阅源

S
SegmentFault 最新的问题
爱范儿
爱范儿
博客园 - 三生石上(FineUI控件)
Microsoft Security Blog
Microsoft Security Blog
Google DeepMind News
Google DeepMind News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
GbyAI
GbyAI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
V2EX
酷 壳 – CoolShell
酷 壳 – CoolShell
量子位
博客园_首页
T
Tailwind CSS Blog
aimingoo的专栏
aimingoo的专栏
A
About on SuperTechFans
T
The Blog of Author Tim Ferriss
Stack Overflow Blog
Stack Overflow Blog
Recent Announcements
Recent Announcements
P
Proofpoint News Feed
博客园 - 司徒正美
有赞技术团队
有赞技术团队
Engineering at Meta
Engineering at Meta
Last Week in AI
Last Week in AI
MongoDB | Blog
MongoDB | 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 330.按要求补齐数组|OhYee 博客
2020-12-29 · via OhYee 博客

LeetCode 330.按要求补齐数组

题目描述

给定一个已排序的正整数数组 numsnums,和一个正整数 nn 。从 [1,n][1, n] 区间内选取任意个数字补充到 numsnums 中,使得 [1,n][1, n] 区间内的任何数字都可以用 numsnums 中某几个数字的和来表示。请输出满足上述要求的最少需要补充的数字个数。

示例 1

输入: nums = [1,3], n = 6
输出: 1
解释:

根据 nums 里现有的组合 [1], [3], [1,3],可以得出 1, 3, 4。
现在如果我们将 2 添加到 nums 中, 组合变为: [1], [2], [3], [1,3], [2,3], [1,2,3]。
其和可以表示数字 1, 2, 3, 4, 5, 6,能够覆盖 [1, 6] 区间里所有的数。
所以我们最少需要添加一个数字。

示例 2

输入: nums = [1,5,10], n = 20
输出: 2
解释:

我们需要添加 [2, 4]。

示例 3

输入: nums = [1,2,2], n = 5
输出: 0

题解

参考了 @小宇 发布在 LeetCode 中的 《简单思路(4行代码,超100%)

对于已知 [1,x][1, x] 都可覆盖,当再给一个数字 yy 时,可以得到新的覆盖区间为 [1,x]∪[1+y,x+y]∪[y,y][1, x] \cup [1+y, x+y] \cup [y, y]
可以将其分为如图两种情况讨论:
(蓝色为原本已覆盖区间,橙色为扩展后覆盖区间,虚线为无法覆盖的区间)

可覆盖区间动画可覆盖区间动画

y→[1,x]={[1,x+y]y≤x[1,x]∪[y,x+y]y>xy \rarr [1, x] = \begin{cases} [1, x+y] & y \le x \\ [1, x] \cup [y, x+y] & y \gt x \end{cases}

只有新的数字 yy 大于原本的右边界时,才会导致可覆盖区间不连续。
需要特别注意的是,原本可覆盖的区间的组成数字可能远小于xx,如只需要 {1,2,4}\{1,2,4\} 即可组成 {1,2,3,4,5,6,7}\{1,2,3,4,5,6,7\}

{1=12=23=1+24=45=1+46=2+47=1+2+4\begin{cases}1=1\\2=2\\3=1+2\\4=4\\5=1+4\\6=2+4\\7=1+2+4\end{cases}

当插入新的数字 55 时,可以获得新的区间 [1,12][1, 12]
{6=1+57=2+58=1+2+59=4+510=1+4+511=2+4+512=1+2+4+5\begin{cases}6=1+5\\7=2+5\\8=1+2+5\\9=4+5\\10=1+4+5\\11=2+4+5\\12=1+2+4+5\end{cases}

mm最小不可覆盖数,则当前已知的可覆盖区间为 [1,m)[1, m)。从小到大判断数组中的元素 nums[i]nums[i] 是否小于等于 mm。如果满足,则说明当前数可以扩展区间至 [1,m+nums[i])[1, m + nums[i]);否则,则说明将会获得区间 [1,m)∪[nums[i],m+nums[i])[1, m)\cup [nums[i], m+nums[i]),存在不可覆盖区间 [m,nums[i])[m, nums[i])
要覆盖这个不可覆盖区间,需要添加一个(或多个)数字,使得区间扩展到 nums[i]nums[i]。要尽可能少添加数字,那么应该尽可能使得扩展的范围更大,同时确保覆盖区间的连续。由上面的图片可知,添加的数字刚好与边界值相等时,扩展的距离最“远”。也即应该添加的数字为当前的边界 mm,扩展后有 m′=m+mm'=m+m。重复该行为,直到 m>nums[i]m>nums[i]
使用上面的策略,可以逐渐增大 mm,最终使得 m>nm>n,也即 [1,n]⊆[1,m)[1, n] \subseteq [1, m) 都可被覆盖

mm 扩大共有两种策略:增加数组中的数,扩大一倍。前者时间复杂度为 O(l)O(l),后者时间复杂度为 O(log(n))O(log(n))。因此,总体的时间复杂度为 O(l+log(n))O(l + log(n))

该题目难点在于,将 寻找最少要插入的数个数 转换为 连续区间扩展,以及区间扩展的两种策略。

代码

Python 代码

class Solution:
    def minPatches(self, nums: List[int], n: int) -> int:
        l = len(nums)
        m = 1
        i = 0
        res = 0
        while m <= n:
            if i < l and nums[i] <= m:
                m += nums[i]
                i += 1
            else:
                m += m
                res += 1
        return res

C 代码

int minPatches(int* nums, int numsSize, int n){
    unsigned int m = 1, res = 0, i = 0;
    while (m <= n) m += (i < numsSize && nums[i] <= m) ? nums[i++] : (res++, m);
    return res;
}