[LeetCode周赛复盘] 第 276 场周赛20220612

[LeetCode周赛复盘] 第 276 场周赛20220612

    • 一、本周周赛总结
    • 二、 [Easy] 5259. 计算应缴税款总额
      • 1. 题目描述
      • 2. 思路分析
      • 3. 代码实现
    • 三、[Medium] 5270. 网格中的最小路径代价
      • 1. 题目描述
      • 2. 思路分析
      • 3. 代码实现
    • 四、[Medium] 5289. 公平分发饼干
      • 1. 题目描述
      • 2. 思路分析
      • 3. 代码实现
    • 五、[Hard] 6094. 公司命名
      • 1. 题目描述
      • 2. 思路分析
      • 3. 代码实现
    • 六、参考链接

一、本周周赛总结

  • 今天状态菜到飞起,不管是周赛还是作业,思路都很僵硬。
  • 前三题都应该早点做出来的,第四题确实不会。
  • 但实际上我就做了1和3,2是结束了才转过弯。
  • 尤其是2,我试了dijkstra和floy都TLE,想了半天为啥50X50的数据规模会炸,后来想起来他们的复杂度我想错了,比如floydO(n^3),n是节点个数,那这题节点个数是2500。。。实际用dp就能做。
  • 做了板子后,思想可能容易僵化,以后一定要引以为戒。
    [LeetCode周赛复盘] 第 276 场周赛20220612_第1张图片

二、 [Easy] 5259. 计算应缴税款总额

链接: 5259. 计算应缴税款总额

1. 题目描述

给你一个下标从 0 开始的二维整数数组 brackets ,其中 brackets[i] = [upperi, percenti] ,表示第 i 个税级的上限是 upperi ,征收的税率为 percenti 。税级按上限 从低到高排序(在满足 0 < i < brackets.length 的前提下,upperi-1 < upperi)。

税款计算方式如下:

  • 不超过 upper0 的收入按税率 percent0 缴纳
  • 接着 upper1 - upper0 的部分按税率 percent1 缴纳
  • 然后 upper2 - upper1 的部分按税率 percent2 缴纳
  • 以此类推
    给你一个整数 income 表示你的总收入。返回你需要缴纳的税款总额。与标准答案误差不超 10-5 的结果将被视作正确答案。

2. 思路分析

送分题,我写了7min,可能这就是菜吧。

3. 代码实现

class Solution:
    def calculateTax(self, brackets: List[List[int]], income: int) -> float:
        tax = 0
        low = 0
        for u,p in brackets:            
            x = min(income,u-low)
            income -= x
            low = u
            # print(x,p)
            tax += x * p/100
        return tax

三、[Medium] 5270. 网格中的最小路径代价

链接: 5270. 网格中的最小路径代价

1. 题目描述

给你一个下标从 0 开始的整数矩阵 grid ,矩阵大小为 m x n ,由从 0 到 m * n - 1 的不同整数组成。你可以在此矩阵中,从一个单元格移动到 下一行 的任何其他单元格。如果你位于单元格 (x, y) ,且满足 x < m - 1 ,你可以移动到 (x + 1, 0), (x + 1, 1), …, (x + 1, n - 1) 中的任何一个单元格。注意: 在最后一行中的单元格不能触发移动。

每次可能的移动都需要付出对应的代价,代价用一个下标从 0 开始的二维数组 moveCost 表示,该数组大小为 (m * n) x n ,其中 moveCost[i][j] 是从值为 i 的单元格移动到下一行第 j 列单元格的代价。从 grid 最后一行的单元格移动的代价可以忽略。

grid 一条路径的代价是:所有路径经过的单元格的 值之和 加上 所有移动的 代价之和 。从 第一行 任意单元格出发,返回到达 最后一行 任意单元格的最小路径代价。
[LeetCode周赛复盘] 第 276 场周赛20220612_第2张图片

2. 思路分析

定级Medium。
其实不难,dp每一行记录到这里的最小代价即可。我在最短路搜索上钻牛角尖了。

定义dp[i][j] 定义为到达坐标i,j的最小代价,那么dp[i][j]一定从上一行转移而来:

  • 转移代价是路径代价+本坐标的代价,即:
  • dp[i][j] = min{dp[i-1][k]+moveCost[grid[i-1][k]][j]+grid[i][j] | k∈[0,n)}

3. 代码实现

class Solution:
    def minPathCost(self, grid: List[List[int]], moveCost: List[List[int]]) -> int:
        m,n =len(grid),len(grid[0])
        dp = [[0]*n for _ in range(m)]
        for i in range(n):
            dp[0][i] = grid[0][i]
        for i in range(1,m):
            for j in range(n):
                dp[i][j] = min(dp[i-1][k]+moveCost[grid[i-1][k]][j]+grid[i][j] for k in range(n))
        return min(dp[m-1])

四、[Medium] 5289. 公平分发饼干

链接: 5289. 公平分发饼干

1. 题目描述

给你一个整数数组 cookies ,其中 cookies[i] 表示在第 i 个零食包中的饼干数量。另给你一个整数 k 表示等待分发零食包的孩子数量,所有 零食包都需要分发。在同一个零食包中的所有饼干都必须分发给同一个孩子,不能分开。

分发的 不公平程度 定义为单个孩子在分发过程中能够获得饼干的最大总数。

返回所有分发的最小不公平程度。
[LeetCode周赛复盘] 第 276 场周赛20220612_第3张图片

2. 思路分析

定级Medium。
平均分配的题,有点像这道: 698. 划分为k个相等的子集。
但不是严格平均,一定可以分配下去。
这题规模不大,8^8,因此可以DFS搜索。
这里吐槽一下,c++不剪枝就能过。python必须剪枝。

  • dfs搜索每盒糖果分配给每个孩子的情况,每个糖果可以选8个孩子中的一个给,因此是8^8种情况。
  • 剪枝:因为是dfs,一定会先搜出一条路径的结果(预处理一个也可以),这个结果意味着当前路径下,最大孩子拥有的数量
    • 下一条路径有孩子的数量>= 这个数就不用搜索了,结果不会更优。
    • 为了早点产生冲突分支,可以把糖果先逆序排序。
    • 还有一个很重要的剪枝是:糖果i给孩子j之前,检测j拥有的糖果数量,前边是否有孩子和他相同,那说明这个方案试过了,可以直接剪掉。
    • 一般可以
      if j>0 and child[j] == child[j-1]:
                          continue
      
    • # 注意一定要逆序,实测快!
      for m in range(j-1,-1,-1):  # 和前边某块一样长,在前边放过了,这次不放了
                  if child[j] == child[m]:
                      break
      

[LeetCode周赛复盘] 第 276 场周赛20220612_第4张图片

3. 代码实现

class Solution:
    def distributeCookies(self, cookies: List[int], k: int) -> int:
        total = sum(cookies)
        n = len(cookies)
        child = [0] * k
        cookies.sort(reverse=True)
        ans = max(cookies[0],sum(cookies[k-1:]))
        def dfs(i):
            nonlocal ans
            if i == n:
                ans = min(ans,max(child))
                return
            for j in range(k):
                if child[j] + cookies[i] > ans:
                    continue
                for m in range(j-1,-1,-1):  # 和前边某块一样长,在前边放过了,这次不放了
                    if child[j] == child[m]:
                        break
                else:
                    child[j] += cookies[i]
                    dfs(i+1)
                    child[j] -= cookies[i]
        
        dfs(0)
        return ans

五、[Hard] 6094. 公司命名

链接: 6094. 公司命名

1. 题目描述

给你一个字符串数组 ideas 表示在公司命名过程中使用的名字列表。公司命名流程如下:

从 ideas 中选择 2 个 不同 名字,称为 ideaA 和 ideaB 。
交换 ideaA 和 ideaB 的首字母。
如果得到的两个新名字 都 不在 ideas 中,那么 ideaA ideaB(串联 ideaA 和 ideaB ,中间用一个空格分隔)是一个有效的公司名字。
否则,不是一个有效的名字。
返回 不同 且有效的公司名字的数目。

2. 思路分析

定级Hard
比赛时没做出来,其实不应该。
用字典中套集合,统计每个字符开头后跟的后缀们。
然后枚举每个开头的组合,
实际是求他们的后缀差集的数量。

3. 代码实现

class Solution:
    def distinctNames(self, ideas: List[str]) -> int:
        d = defaultdict(set)
        for name in ideas: d[name[0]].add(name[1:])
        return sum((len(d[a]) - len(d[a] & d[b])) * (len(d[b]) - len(d[a] & d[b])) * 2 for a, b in product(d, repeat=2) if ord(a) < ord(b))

六、参考链接

  • 链接: [LeetCode解题报告] 698. 划分为k个相等的子集

你可能感兴趣的:(今天开刷leetcode,力扣周赛复盘,动态规划,算法,leetcode)