【刷穿 LeetCode】1894. 找到需要补充粉笔的学生编号 :「前缀和 + 二分」&「模拟」

简介: 【刷穿 LeetCode】1894. 找到需要补充粉笔的学生编号 :「前缀和 + 二分」&「模拟」

网络异常,图片无法展示
|

题目描述



这是 LeetCode 上的 1894. 找到需要补充粉笔的学生编号 ,难度为 中等


Tag : 「前缀和」、「二分」、「模拟」


一个班级里有 n 个学生,编号为 0 到 n - 1 。每个学生会依次回答问题,编号为 0 的学生先回答,然后是编号为 1 的学生,以此类推,直到编号为 n - 1 的学生,然后老师会重复这个过程,重新从编号为 0 的学生开始回答问题。


给你一个长度为 n 且下标从 0 开始的整数数组 chalk 和一个整数 k 。一开始粉笔盒里总共有 k 支粉笔。当编号为 i 的学生回答问题时,他会消耗 chalk[i] 支粉笔。如果剩余粉笔数量 严格小于 chalk[i] ,那么学生 i 需要 补充 粉笔。


请你返回需要 补充 粉笔的学生 编号


示例 1:


输入:chalk = [5,1,5], k = 22
输出:0
解释:学生消耗粉笔情况如下:
- 编号为 0 的学生使用 5 支粉笔,然后 k = 17 。
- 编号为 1 的学生使用 1 支粉笔,然后 k = 16 。
- 编号为 2 的学生使用 5 支粉笔,然后 k = 11 。
- 编号为 0 的学生使用 5 支粉笔,然后 k = 6 。
- 编号为 1 的学生使用 1 支粉笔,然后 k = 5 。
- 编号为 2 的学生使用 5 支粉笔,然后 k = 0 。
编号为 0 的学生没有足够的粉笔,所以他需要补充粉笔。
复制代码


示例 2:


输入:chalk = [3,4,1,2], k = 25
输出:1
解释:学生消耗粉笔情况如下:
- 编号为 0 的学生使用 3 支粉笔,然后 k = 22 。
- 编号为 1 的学生使用 4 支粉笔,然后 k = 18 。
- 编号为 2 的学生使用 1 支粉笔,然后 k = 17 。
- 编号为 3 的学生使用 2 支粉笔,然后 k = 15 。
- 编号为 0 的学生使用 3 支粉笔,然后 k = 12 。
- 编号为 1 的学生使用 4 支粉笔,然后 k = 8 。
- 编号为 2 的学生使用 1 支粉笔,然后 k = 7 。
- 编号为 3 的学生使用 2 支粉笔,然后 k = 5 。
- 编号为 0 的学生使用 3 支粉笔,然后 k = 2 。
编号为 1 的学生没有足够的粉笔,所以他需要补充粉笔。
复制代码


提示:


  • chalk.length == n
  • 1 <= n <= 10^5105
  • 1 <= chalk[i] <= 10^5105
  • 1 <= k <= 10^9109


前缀和 + 二分



根据题意,每个学生消耗的粉笔为定值,所有粉笔最终会像老师的教导一样孜孜不倦地循环投入在所有的学生身上。


因此我们可以预处理出前缀和数组 sumsum,将 kk 对所有学生一次循环所消耗总粉笔数(sum[n]sum[n])进行取模,得到最后一轮开始前的粉笔数量。


然后对前缀和数组进行二分,找到最后一位满足粉笔要求的学生,其往后一位的同学编号即是答案。


代码:


class Solution {
    public int chalkReplacer(int[] chalk, int k) {
        int n = chalk.length;
        long[] sum = new long[n + 1];
        for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + chalk[i - 1];
        k = (int)(k % sum[n]);
        int l = 1, r = n;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (sum[mid] <= k) l = mid;
            else r = mid - 1;
        }
        return sum[r] <= k ? r : r - 1;
    }
}
复制代码


  • 时间复杂度:预处理前缀和的复杂度为 O(n)O(n);二分求答案的复杂度为 O(\log{n})O(logn)。整体复杂度为 O(n)O(n)
  • 空间复杂度:O(n)O(n)


模拟



通过解法一,我们发现复杂度的上界为预处理前缀和的复杂度 O(n)O(n),同时「对单次循环消耗的总粉笔数取模操作」确保了剩余的粉笔数必然会在单次遍历中消耗完成。


因此 O(\log{n})O(logn) 的二分其实是没有必要的,只需要再对 chalk 进行最后一轮的遍历模拟即可。


代码:


class Solution {
    public int chalkReplacer(int[] chalk, int k) {
        int n = chalk.length;
        long max = 0;
        for (int i : chalk) max += i;
        k = (int)(k % max);
        for (int i = 0; i < n; i++) {
            k -= chalk[i];
            if (k < 0) return i;
        }
        return -1; // never
    }
}
复制代码


  • 时间复杂度:O(n)O(n)
  • 空间复杂度:O(1)O(1)


最后



这是我们「刷穿 LeetCode」系列文章的第 No.1894 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。


在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。


为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour…


在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

相关文章
|
算法 Android开发 索引
LeetCode 周赛上分之旅 #44 同余前缀和问题与经典倍增 LCA 算法
学习数据结构与算法的关键在于掌握问题背后的算法思维框架,你的思考越抽象,它能覆盖的问题域就越广,理解难度也更复杂。在这个专栏里,小彭与你分享每场 LeetCode 周赛的解题报告,一起体会上分之旅。
81 0
|
5月前
【LeetCode刷题】前缀和解决问题:742.寻找数组的中心下标、238.除自身以外数组的乘积
【LeetCode刷题】前缀和解决问题:742.寻找数组的中心下标、238.除自身以外数组的乘积
LeetCode-798 得分最高的最小论调 及差分和前缀和的学习
LeetCode-798 得分最高的最小论调 及差分和前缀和的学习
|
6月前
|
自然语言处理 索引
leetcode-745:前缀和后缀搜索
leetcode-745:前缀和后缀搜索
60 0
|
6月前
[leetcode 前缀和]
[leetcode 前缀和]
|
算法 索引
LeetCode算法小抄--数组(双指针、差分数组、前缀和)
LeetCode算法小抄--数组(双指针、差分数组、前缀和)
|
人工智能 算法 搜索推荐
LeetCode 周赛 338,贪心 / 埃氏筛 / 欧氏线性筛 / 前缀和 / 二分查找 / 拓扑排序
大家好,我是小彭。 上周末是 LeetCode 第 338 场周赛,你参加了吗?这场周赛覆盖的知识点很多,第四题称得上是近期几场周赛的天花板。
111 0
|
机器学习/深度学习 人工智能
|
算法 索引
leetcode-每日一题745. 前缀和后缀搜索(哈希和字典树)
如果我们用前缀 prefix 和 后缀 suff去暴力对比所有单词肯定会超时,我们可以先把单词里所有的前缀后缀组合,中间用特殊符号@连接,对应的最大下标存入哈希表中。搜索时,用特殊符号@连接前缀后缀,在哈希表中进行搜索
95 0
leetcode-每日一题745. 前缀和后缀搜索(哈希和字典树)
|
算法
LeetCode 周赛 340,质数 / 前缀和 / 极大化最小值 / 最短路 / 平衡二叉树
今天讲 LeetCode 单周赛第 340 场,今天状态不好,掉了一波大分。
104 0