[LeetCode周赛复盘] 第 340 场周赛20230409

[LeetCode周赛复盘] 第 340 场周赛20230409

    • 一、本周周赛总结
    • 二、 6361. 对角线上的质数
      • 1. 题目描述
      • 2. 思路分析
      • 3. 代码实现
    • 三、6360. 等值距离和
      • 1. 题目描述
      • 2. 思路分析
      • 3. 代码实现
    • 四、6359. 最小化数对的最大差值
      • 1. 题目描述
      • 2. 思路分析
      • 3. 代码实现
    • 五、 6353. 网格图中最少访问的格子数
      • 1. 题目描述
      • 2. 思路分析
      • 3. 代码实现。
    • 六、参考链接

一、本周周赛总结

  • 这周都还挺难的。
  • T1 判断质数。
  • T2 预处理+前缀和+二分。
  • T3 二分答案。
  • T4 并查集优化剪枝转移状态的BFS(和上周一样)。

[LeetCode周赛复盘] 第 340 场周赛20230409_第1张图片

二、 6361. 对角线上的质数

链接: 6361. 对角线上的质数

1. 题目描述

[LeetCode周赛复盘] 第 340 场周赛20230409_第2张图片

2. 思路分析

按题意模拟即可。

  • 由于数据范围4e6,没敢上筛;但是n小,所以暴力了(其实可以上筛);结果没判断1是合数,结果wa了。。

  • 这题证明了py的埃氏筛就是比欧拉筛快。
  • [LeetCode周赛复盘] 第 340 场周赛20230409_第3张图片
    [LeetCode周赛复盘] 第 340 场周赛20230409_第4张图片
  • [LeetCode周赛复盘] 第 340 场周赛20230409_第5张图片

3. 代码实现


class Solution:
    def diagonalPrime(self, nums: List[List[int]]) -> int:
        p = set()
        m,n = len(nums),len(nums[0])
        for i in range(n):
            p.add(nums[i][i])
            p.add(nums[i][n-i-1])
            
        def is_prime(x):            
            if x <=1:
                return False
            if x == 2 or x == 3:
                return True 
            for i in range(2,int(x**0.5)+1):
                if x % i ==0:
                    return False 
            return True
           
            
        ans = 0
        for x in p:
            if is_prime(x):
                ans = max(ans,x)
        return ans

三、6360. 等值距离和

链接: 6360. 等值距离和

1. 题目描述

[LeetCode周赛复盘] 第 340 场周赛20230409_第6张图片

2. 思路分析

  • 一看考过差不多的,但是要分组。
  • 忘记预处理Tle。。
  • 按值分组,记录下标列表。
  • 对每个位置i,二分对应的列表找到i的位置。
  • 那么ans[i]可以用前缀和/后缀和计算了。

3. 代码实现

class Solution:
    def distance(self, nums: List[int]) -> List[int]:
        n = len(nums)
        d = defaultdict(list)
        for i,v in enumerate(nums):
            d[v].append(i)
        pres = defaultdict(list)
        for k,v in d.items():
            pres[k] =  [0] + list(accumulate(v))
        ans = [0] * n
        for i,v in enumerate(nums):
            p = d[v]
            n = len(p)
            if len(p)<=1:
                continue 
            pre = pres[v]
            pos = bisect_left(p,i)
            # print(p,pre,pos)
            ans[i] = i*(pos+1) - pre[pos+1] + pre[-1] - pre[pos+1] - i*(n-pos-1)
        return ans

四、6359. 最小化数对的最大差值

链接: 6359. 最小化数对的最大差值

1. 题目描述

[LeetCode周赛复盘] 第 340 场周赛20230409_第7张图片

2. 思路分析

最大值最小化,警觉。
  • 最大值最小化等价于二分答案。
  • 设f(x)为选出的序列中最大差值不超过x时,能否找到s对序列。
  • 显然,若f(x)==True,则f(x+1)一定是True,f(x-1)不一定,因此有二段性。找到最小的x即可。
  • f(x)怎么写呢,要让差值尽可能小,考虑每个数,排序后和相邻的数据组合就是最小值。
  • 贪心的试图组合每个数即可。你可能会质疑贪心的正确性:考虑(a,b,c,d),若ab能组合,那会使选出的对+1;如果不组合,b也只可能跟后边的c组合,而且占用了c,不会使答案更优。

3. 代码实现

class Solution:
    def minimizeMax(self, nums: List[int], p: int) -> int:
        n = len(nums)
        nums.sort()
        
        def ok(x):
            s = 0
            i  = 0
            while i < n-1:
                if nums[i+1] - nums[i] <= x:
                    s += 1
                    i += 1
                i += 1
            return s >= p 
        
        return bisect_left(range(10**9+1),True,key=ok)    

五、 6353. 网格图中最少访问的格子数

链接: 6353. 网格图中最少访问的格子数

1. 题目描述

[LeetCode周赛复盘] 第 340 场周赛20230409_第8张图片

2. 思路分析

没想到出了上周T4的加强版。
  • 乍一看是普通BFS最短路,但是由于转移目标是一个range(注意是连续的范围),暴力转移可能会TLE。
  • 因此可以用有序集合或者并查集删除,转移时直接通过数据结构找下一个转移目标,这样每个目标只会被访问一次,就可以AC啦!
  • 时间复杂度O(nm),这里认为并查集的均摊复杂度是O(1)。

  • 对每行每列都建立独立的并查集,一共m+n个。
  • 当访问(x,y)后,把它和右边/下边的坐标连接,union对应的(x,y+1)和(x+1,y+1),这样就等于删除(x,y)。
  • 我提交的版本没有连接另一个方向的坐标,也过了,但是跑得慢一点。应该是不影响正确性的,但每个坐标要访问两次了。

3. 代码实现。

class DSU:
    def __init__(self, n):
        self.fathers = list(range(n))

    def find_fa(self, x):
        fs = self.fathers
        t = x
        while fs[x] != x:
            x = fs[x]
        while t != x:
            fs[t], t = x, fs[t]
        return x

    def union(self, x: int, y: int) -> bool:
        x = self.find_fa(x)
        y = self.find_fa(y)

        if x == y:
            return False
        # if self.size[x] > self.size[y]:  # 注意如果要定向合并x->y,需要干掉这个;实际上上边改成find_fa后,按轶合并没必要了,所以可以常关
        #     x, y = y, x
        self.fathers[x] = y
        return True

class Solution:
    def minimumVisitedCells(self, grid: List[List[int]]) -> int:
        m,n = len(grid),len(grid[0])
        if m==n==1:
            return 1
        def inside(x,y):
            return 0<=x<m and 0<=y<n
        rows = [DSU(n+1) for _ in range(m)]
        cols = [DSU(m+1) for _ in range(n)]
        ans = 1
        q = deque([(0,0)])
        rows[0].union(0,1)
        cols[0].union(0,1)
        while q:
            ans += 1
            for _ in range(len(q)):
                x,y = q.popleft()
                if x == m-1 and y == n-1:
                    return ans - 1
                mn,mx = min(y+1,n-1),min(grid[x][y]+y,n-1)
                dsu1,dsu2 = rows[x],cols[y]
                while mn <= mx:
                    mn = dsu1.find_fa(mn)
                    if mn <= mx:
                        if x == m-1 and mn == n-1:
                            return ans
                        q.append((x,mn))
                        dsu1.union(mn,mn+1)
                        cols[mn].union(x,x+1)
                        mn += 1
                        
                mn,mx = min(x+1,m-1),min(grid[x][y]+x,m-1)
                
                while mn <= mx:
                    mn = dsu2.find_fa(mn)
                    if mn <= mx:
                        if mn == m-1 and y == n-1:
                            return ans
                        q.append((mn,y))
                        dsu2.union(mn,mn+1)
                        rows[mn].union(y,y+1)
                        mn += 1                                
                
        return -1       

六、参考链接

你可能感兴趣的:(力扣周赛复盘,leetcode,算法,职场和发展)