[LeetCode周赛复盘] 第 338 场周赛20230326
一、本周周赛总结
- 不敢藏题了,哭唧唧。
- T1 模拟。
- T2 贪心+二分。
- T3 排序+前缀和+双指针/二分。
- T4 拓扑排序剪树枝。
二、 [Easy] 6354. K 件物品的最大和
链接: 6354. K 件物品的最大和
1. 题目描述
2. 思路分析
- 由于数据范围比较小,其实造数组取前k个可能是更好的方案。
3. 代码实现
class Solution:
def kItemsWithMaximumSum(self, numOnes: int, numZeros: int, numNegOnes: int, k: int) -> int:
ans = 0
if k >= numOnes:
ans += numOnes
k -= numOnes
else:
ans += k
return ans
if k >= numZeros:
k -= numZeros
else:
return ans
if k:
ans -= k
return ans
三、[Medium] 6355. 质数减法运算
链接: 6355. 质数减法运算
1. 题目描述
2. 思路分析
贪心
- 显然最终目的要让前边的数尽量小。
- 那么从前往后遍历,让每个数尽量小,则减去尽量大的那个质数即可。
- 设置完第一数字,然后遍历后边的,计算和前一个数的差d,找小于d的最大质数,可以二分。
3. 代码实现
def tag_primes_eratosthenes(n): # 返回一个长度n的数组p,如果i是质数则p[i]=1否则p[i]=0
primes = [1]*n
primes[0] = primes[1] = 0 # 0和1不是质数
for i in range(2,int(n**0.5)+1):
if primes[i]:
primes[i * i::i] = [0] * ((n - 1 - i * i) // i + 1)
return primes
primes = tag_primes_eratosthenes(10**3+5)
ps = [i for i in range(2,10**3+5) if primes[i]]
class Solution:
def primeSubOperation(self, nums: List[int]) -> bool:
n = len(nums)
p = bisect_left(ps,nums[0]) - 1
if p >= 0:
nums[0] -= ps[p]
for i in range(1,n):
v = nums[i]
if v <= nums[i-1]:
return False
d = v - nums[i-1]
p = bisect_left(ps,d) - 1
if p >= 0:
nums[i] -= ps[p]
return True
四、[Medium] 6357. 使数组元素全部相等的最少操作次数
1. 题目描述
2. 思路分析
排序+双指针递推。
- 对每个q来说,所有<=q的a[i](假设有j个)都可以用q-a[i]计算,那么求和就是j*q-s[i] (前缀和)。
- 其余部分是a[i]-q,后缀和-(n-j)*q。
- 那么其实可以对询问离线,然后排序双指针。这样可以递推时顺便求前缀和。
- 然而其实只要nums排序了,询问不需要离线排序,直接对nums预处理前缀和二分即可。
3. 代码实现
class Solution:
def minOperations(self, nums: List[int], queries: List[int]) -> List[int]:
n ,m= len(nums), len(queries)
nums.sort()
s = sum(nums)
p = 0
ans = [0]*m
j = 0
for v,i in sorted((v,i) for i,v in enumerate(queries)):
while j<n and nums[j] <= v:
p += nums[j]
j += 1
ans[i] = j*v - p + s-p -v*(n-j)
return ans
五、[Hard] 6356. 收集树中金币
链接: 6356. 收集树中金币
1. 题目描述
2. 思路分析
本题有换根dp做法、树上dp做法,最好理解的还是这个拓扑排序。
- 首先容易想到,可以把所有不含金币的树枝剪去,方法是拓扑排序,从degree为1的节点(叶子)开始,且这个节点不含金币。逐步把所有不含金币且在枝上的节点剪去。
- 第一步完成后,剩下的图的叶子一定都是含有金币的。
- 从这些叶子继续往里剪两次,注意,不是按多源bfs距离<2剪,而是剪两轮叶子。即执行两轮拓扑排序的剪度。
- 他们的区别是:若存在一个bfs距离为1的节点,含3个度,它只有在第一次拓扑后,至少2个邻居都被剪掉了,它才会被在第二次剪掉,因为它在这时已经变成
孤零零
;否则它则需要担负起作为连接一条通路的中间节点
的重任,不能被剪掉。
3. 代码实现
class Solution:
def collectTheCoins(self, coins: List[int], edges: List[List[int]]) -> int:
n = len(coins)
ca = sum(coins)
if ca <= 1:
return 0
degree = [0]*n
g = [[] for _ in range(n)]
for u,v in edges:
g[u].append(v)
g[v].append(u)
degree[u] += 1
degree[v] += 1
q = deque([i for i,v in enumerate(degree) if v==1 and coins[i] == 0])
vis = set(q)
while q:
u = q.popleft()
vis.add(u)
for v in g[u]:
degree[v] -= 1
if degree[v] == 1 and coins[v] == 0:
q.append(v)
time = [0]*n
q = deque([i for i,v in enumerate(degree) if v==1 and coins[i] == 1])
step = 0
while q:
for _ in range(len(q)):
u = q.popleft()
for v in g[u]:
degree[v] -= 1
if v in vis :
continue
if degree[v] == 1:
time[v] = time[u] + 1
q.append(v)
step += 1
ans = 0
for u,v in edges:
if time[u] >= 2 and time[v]>=2:
ans += 2
return ans