周赛日期:2020/12/26
学校的自助午餐提供圆形和方形的三明治,分别用数字 0 和 1 表示。所有学生站在一个队列里,每个学生要么喜欢圆形的要么喜欢方形的。
餐厅里三明治的数量与学生的数量相同。所有三明治都放在一个 栈 里,每一轮:如果队列最前面的学生 喜欢 栈顶的三明治,那么会 拿走它 并离开队列。
否则,这名学生会 放弃这个三明治 并回到队列的尾部。
这个过程会一直持续到队列里所有学生都不喜欢栈顶的三明治为止。给你两个整数数组 students 和 sandwiches ,其中 sandwiches[i] 是栈里面第 i 个三明治的类型(i = 0 是栈的顶部), students[j] 是初始队列里第 j 名学生对三明治的喜好(j = 0 是队列的最开始位置)。请你返回无法吃午餐的学生数量。
示例 1:
输入:students = [1,1,0,0], sandwiches = [0,1,0,1]
输出:0
解释:
- 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [1,0,0,1]。
- 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [0,0,1,1]。
- 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [0,1,1],三明治栈为 sandwiches = [1,0,1]。
- 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [1,1,0]。
- 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [1,0],三明治栈为 sandwiches = [0,1]。
- 最前面的学生放弃最顶上的三明治,并回到队列的末尾,学生队列变为 students = [0,1]。
- 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [1],三明治栈为 sandwiches = [1]。
- 最前面的学生拿走最顶上的三明治,剩余学生队列为 students = [],三明治栈为 sandwiches = []。
所以所有学生都有三明治吃。
示例 2:输入:students = [1,1,1,0,0,1], sandwiches = [1,0,0,0,1,1]
输出:3
提示:
1 <= students.length, sandwiches.length <= 100
students.length == sandwiches.length
sandwiches[i] 要么是 0 ,要么是 1 。
students[i] 要么是 0 ,要么是 1 。
本题最简单的做法就是指针+模拟,但其实有更简单的做法,时间复杂度为o(n)。
我们先想想什么情况下,会有同学无法吃饭,
举个例子,当students=[0,0,0] sandwitches = [1, 0, 0]的时候sandwitches就无法再出栈,处于了卡死状态,则有三个学生没法吃饭,
反之,如果此时students中有一个1,那么还可以继续,所以只需要一开始统计学生0和1的数量,然后不断出栈sandwiches,直到“卡死”或者所有三明治出栈。
“卡死”状态可以具体为:已经没有同学喜欢当前栈顶种类的三明治了,即count[sandwiched[0]] == 0。最后剩余的了students的数量就是吃不到三明治的人数。
class Solution:
def countStudents(self, students: List[int], sandwiches: List[int]) -> int:
count = [0, 0]
for s in students:
count[s] += 1
for sandwich in sandwiches:
if count[sandwich] == 0: break # "卡死"
count[sandwich] -= 1
return count[1] + count[0]
有一个餐厅,只有一位厨师。你有一个顾客数组 customers ,其中 customers[i] = [arrivali, timei] :
arrivali 是第 i 位顾客到达的时间,到达时间按 非递减 顺序排列。
timei 是给第 i 位顾客做菜需要的时间。
当一位顾客到达时,他将他的订单给厨师,厨师一旦空闲的时候就开始做这位顾客的菜。每位顾客会一直等待到厨师完成他的订单。厨师同时只能做一个人的订单。厨师会严格按照 订单给他的顺序 做菜。请你返回所有顾客需要等待的 平均 时间。与标准答案误差在 10-5 范围以内,都视为正确结果。
示例 1:
输入:customers = [[1,2],[2,5],[4,3]]
输出:5.00000
解释:
1) 第一位顾客在时刻 1 到达,厨师拿到他的订单并在时刻 1 立马开始做菜,并在时刻 3 完成,第一位顾客等待时间为 3 - 1 = 2 。
2) 第二位顾客在时刻 2 到达,厨师在时刻 3 开始为他做菜,并在时刻 8 完成,第二位顾客等待时间为 8 - 2 = 6 。
3) 第三位顾客在时刻 4 到达,厨师在时刻 8 开始为他做菜,并在时刻 11 完成,第三位顾客等待时间为 11 - 4 = 7 。
平均等待时间为 (2 + 6 + 7) / 3 = 5 。
示例 2:输入:customers = [[5,2],[5,4],[10,3],[20,1]]
输出:3.25000
解释:
1) 第一位顾客在时刻 5 到达,厨师拿到他的订单并在时刻 5 立马开始做菜,并在时刻 7 完成,第一位顾客等待时间为 7 - 5 = 2 。
2) 第二位顾客在时刻 5 到达,厨师在时刻 7 开始为他做菜,并在时刻 11 完成,第二位顾客等待时间为 11 - 5 = 6 。
3) 第三位顾客在时刻 10 到达,厨师在时刻 11 开始为他做菜,并在时刻 14 完成,第三位顾客等待时间为 14 - 10 = 4 。
4) 第四位顾客在时刻 20 到达,厨师拿到他的订单并在时刻 20 立马开始做菜,并在时刻 21 完成,第四位顾客等待时间为 21 - 20 = 1 。
平均等待时间为 (2 + 6 + 4 + 1) / 4 = 3.25 。
提示:
1 <= customers.length <= 105
1 <= arrivali, timei <= 104
arrivali <= arrivali+1
处理的顺序题目已经告知,当有个顾客来,当前厨师要是空闲,就直接开始做他的订单,如果厨师非空闲,就只能等待他现在和之前等待的人都做好了,再开始他的订单。
所以用cur_time表示厨师的最近的空闲时间,
从头遍历顾客,对于当前顾客,
class Solution:
def averageWaitingTime(self, customers: List[List[int]]) -> float:
if not customers: return 0.0
cur_time = 0
res = 0
for arrival, time in customers:
if cur_time <= arrival:
cur_time = arrival + time
res += time
else:
cur_time += time
res += cur_time - arrival
return res / len(customers)
给你一个二进制字符串 binary ,它仅有 0 或者 1 组成。你可以使用下面的操作任意次对它进行修改:
操作 1 :如果二进制串包含子字符串 "00" ,你可以用 "10" 将其替换。
比方说, "00010" -> "10010"
操作 2 :如果二进制串包含子字符串 "10" ,你可以用 "01" 将其替换。
比方说, "00010" -> "00001"
请你返回执行上述操作任意次以后能得到的 最大二进制字符串 。如果二进制字符串 x 对应的十进制数字大于二进制字符串 y 对应的十进制数字,那么我们称二进制字符串 x 大于二进制字符串 y 。
示例 1:
输入:binary = "000110"
输出:"111011"
解释:一个可行的转换为:
"000110" -> "000101"
"000101" -> "100101"
"100101" -> "110101"
"110101" -> "110011"
"110011" -> "111011"
示例 2:输入:binary = "01"
输出:"01"
解释:"01" 没办法进行任何转换。
提示:
1 <= binary.length <= 105
binary 仅包含 '0' 和 '1' 。
为了得到最大值,我们一定要让最高位尽量变为1。
我们可以发现一个规律:如果存在多个0,最后结果一定只存在一个0,如果存在2个0以上,那么第一个0后面的每一个0都可以让第一个0后移1位。
所以答案为第一个0的位置加上总共0的个数-1位为0,其余位都为1。
class Solution:
def maximumBinaryString(self, binary: str) -> str:
cnt = 0
first = -1
for i in range(len(binary)):
if binary[i] == '0':
if first == -1: first = i
else: cnt += 1
if cnt:
res = list('1'*len(binary))
res[first+cnt] = '0'
return "".join(res)
else: return binary
给你一个整数数组 nums 和一个整数 k 。 nums 仅包含 0 和 1 。每一次移动,你可以选择 相邻 两个数字并将它们交换。
请你返回使 nums 中包含 k 个 连续 1 的 最少 交换次数。
示例 1:
输入:nums = [1,0,0,1,0,1], k = 2
输出:1
解释:在第一次操作时,nums 可以变成 [1,0,0,0,1,1] 得到连续两个 1 。
示例 2:输入:nums = [1,0,0,0,0,0,1,1], k = 3
输出:5
解释:通过 5 次操作,最左边的 1 可以移到右边直到 nums 变为 [0,0,0,0,0,1,1,1] 。
示例 3:输入:nums = [1,1,0,1], k = 2
输出:0
解释:nums 已经有连续 2 个 1 了。
提示:
1 <= nums.length <= 105
nums[i] 要么是 0 ,要么是 1 。
1 <= k <= sum(nums)
记录1的位置,放在数组a中,假设连续的第一个位置为x,那么结果为:
|a0-x| + |a1-(x+1)| + ... + |ak-(x+k)|
我们把a中的元素ai映射为ai-i,那么根据贪心的结论,我们只需要计算把v中的连续k个数移动到中位数位置上的元素的步数和,取最小值。
计算步数和可以用前缀和优化。
然后计算代价:
mid左边:(a[mid] - a[l])+ (a[mid] - a[l+1]) + ...... + (a[mid] - a[mid-1]) = a[mid] * (mid - l) + s[mid-1] - s[l-1]
mid右边:(a[mid+1] - a[mid])+ (a[mid+2] - a[mid]) + ...... + (a[r] - a[mid]) = s[r] - s[mid] - a[mid] * (r - mid)
总代价:mid左边 + mid右边
class Solution:
def minMoves(self, nums: List[int], k: int) -> int:
a = []
j = 0
for i in range(len(nums)):
if nums[i] == 1:
a.append(i-j)
j += 1
s = [0]*(len(a)+1)
a = [0] + a
for i in range(1, len(a)):
s[i] = s[i-1]+a[i] # 前缀和
res = float('inf')
for l in range(1, len(a)-k+1):
r = l + k - 1
mid = l + r >> 1
x = a[mid]
left = x * (mid-l) - (s[mid-1] - s[l-1])
right = s[r] - s[mid] - x * (r-mid)
res = min(res, left + right)
return res