[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. 使数组元素全部相等的最少操作次数

链接: 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   

六、参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值