[M前缀和] lc1894. 找到需要补充粉笔的学生编号(二分+模拟+坑点)
Posted Ypuyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[M前缀和] lc1894. 找到需要补充粉笔的学生编号(二分+模拟+坑点)相关的知识,希望对你有一定的参考价值。
1. 题目来源
2. 题目解析
有个坑点,有点恶心。首先被数据坑一把,需要开 LL,然后被边界坑一把…
先求前缀和,然后 k
对 a[n-1]
取模,然后二分出小于等于 k
的最大值即可。注意,然后就被坑了。 因为小于等于 k
意味着当前同学可以用完粉笔,下一个同学不能用完粉笔,然后就需要返回 l+1
。然而在 k
在对 a[n-1]
取模的时候,以及初始情况都可能导致 k<a[0]
,此时当然应该返回 a[0]
,然而直接返回 a[l+1]
等价于返回 a[1]
肯定是错误的。
所以,二分完之后,a[l]
并不一定是小于等于 k
的,也就是 l+1
这个人并不一定是最终答案,l
也可能是答案。
当然,得到总和,取模之后完全不必要二分。直接遍历一遍,到哪个人粉笔不够了,当然就要输出那个人的编号了。这样就不需要额外开空间了。
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
class Solution {
public:
int chalkReplacer(vector<int>& chalk, int k) {
using LL = long long;
int n = chalk.size();
vector<LL> a;
for (auto e : chalk) a.push_back(e);
for (int i = 1; i < n; i ++ ) a[i] += a[i - 1];
k %= a[n - 1];
int l = 0, r = n - 1;
while (l < r) { // 二分小于等于 k 的最大的一个位置
int mid = l + r + 1 >> 1;
if (a[mid] > k) r = mid - 1;
else l = mid;
}
// 如果小于等于 k,那么是下一个同学铅笔不够,即返回l+1,否则就是本同学铅笔不够
// 值得注意的是,二分停下来的位置按理说一定是 a[l]<=k 的
// 下面这个三目运算符怎么会取到 a[l]>k 的情况呢?
// 实际上因为有 k%=a[n - 1]这个操作,可能会导致 a[0]>k,导致需要返回 0 下标,
// 所有的 a[mid]>k,导致右边界一直缩小到 l,此时 l=r=0
// 所以唯一的情况就是当第一个 a[0]>k 时,二分才会停在 0 的位置,返回l=r=0即可,
// 其它情况都是 a[l]<=k,需要返回 l+1 或者 r+1
// if (a[0] > k) return 0;
// return a[l] <= k ? l + 1 : l - 1000; 后面的不会被执行到
return a[l] <= k ? l + 1 : l;
}
};
go 代码
func chalkReplacer(chalk []int, k int) int {
n := len(chalk)
sum := 0
for i := 0; i < n; i ++ {
sum += chalk[i]
}
k %= sum
for i := 0; i < n; i ++ {
if chalk[i] > k {
return i
}
k -= chalk[i]
}
return -1
}
以上是关于[M前缀和] lc1894. 找到需要补充粉笔的学生编号(二分+模拟+坑点)的主要内容,如果未能解决你的问题,请参考以下文章
Leetcode刷题100天—1894. 找到需要补充粉笔的学生编号( 数组)—day34