LeetCode笔记:Weekly Contest 220 比赛记录
0. 赛后总结这次的比赛结果略惨,只做出3题,然后国内才只有229/3690,世界则是667/9606,都是只有前7%的水平。
但是这次的题目应该算是比较简单的,看了一下,第一名的大佬只花了不到10分钟就搞定了,而我没有做出来的最后一题,事实上就是一个DSU的问题,之前我也专门写博客聊过这个算法(Python笔记:并查集(DSU)结构简介),然而在比赛中还是没有搞出来。
倒不是完全没有想到过使用DSU,但是终究还是想岔了,没能想到正确的使用方法,导致最后一题没有做出来,不过这个后面再谈吧,唉。。。
1. 题目一给出题目一的试题链接如下:
5629. 重新格式化电话号码1. 解题思路这一题挺直接的,解题思路上就是先去掉空格和-字符,然后按照3个数字为一组进行分割,知道最后几位进行特殊处理即可。
2. 代码实现给出python代码实现如下:
代码语言:javascript复制class Solution:
def reformatNumber(self, number: str) -> str:
number = number.replace("-", "").replace(" ", "")
n = len(number)
res = []
idx = 0
while idx < n-4:
res.append(number[idx:idx+3])
idx += 3
if n - idx == 4:
res.extend([number[idx:idx+2], number[idx+2:idx+4]])
else:
res.append(number[idx:])
return "-".join(res)提交代码评测得到:耗时36ms,占用内存14.1MB。
当前的最优算法耗时28ms,但看了一下思路是一致的,因此这里就不过多展开了。
2. 题目二给出题目二的试题链接如下:
5630. 删除子数组的最大得分1. 解题思路这一题的思路其实还是蛮明确的,就是维护一个滑动窗口,确保窗口中不会存在重复的元素。
2. 代码实现给出python代码实现如下:
代码语言:javascript复制class Solution:
def maximumUniqueSubarray(self, nums: List[int]) -> int:
i, j, n = 0, 0, len(nums)
counter = defaultdict(int)
ans = 0
score = 0
while j < n:
while i < j and counter[nums[j]] == 1:
score -= nums[i]
counter[nums[i]] -= 1
i += 1
while j < n and counter[nums[j]] == 0:
score += nums[j]
counter[nums[j]] += 1
j += 1
ans = max(ans, score)
return ans提交代码评测得到:耗时1380ms,占用内存28.4MB。
当前的最优算法耗时792ms,但是看了一下,他们的算法思路和我们是相同的,因此这里就不过多展开了。
3. 题目三给出题目三的试题链接如下:
5631. 跳跃游戏 VI1. 解题思路这一题的思路就是使用动态规划算法。
他的递推关系还是非常明确的,可以给出递推关系如下:
代码语言:txt复制 f ( i ) = n i + m a x ( f ( j ) ) ( i < j ≤ i + k ) f(i) = n\_i + max(f(j)) \ (i< j \leq i+k) f(i)=ni+max(f(j)) (i 2. 代码实现我们直接给出python代码实现如下: 代码语言:javascript复制class Solution: def maxResult(self, nums: List[int], k: int) -> int: n = len(nums) dp = [k for k in nums] q = [(nums[-1], n-1)] for i in range(n-2, -1, -1): while q != [] and q[-1][1] > i + k: q.pop() dp[i] += q[-1][0] while q != [] and q[0][0] < dp[i]: q.pop(0) q.insert(0, (dp[i], i)) return dp[0]提交代码评测得到:耗时828ms,占用内存27.8MB。 当前最优的代码实现耗时704ms,但是看了一下他们的思路和我们是完全一致的,因此这里就不多做展开了。 4. 题目四给出题目四的试题链接如下: 5632. 检查边长度限制的路径是否存在1. 解题思路这题比赛的时候没做出来真的是太伤了,赛后看了一下awice大神的解答,发现我没做出来真的是太菜了。 这题的思路其实挺直白的,显然由于n最大可以取到 1 0 5 10^5 105,因此要求的算法复杂度肯定不能超过 O ( N 2 ) O(N^2) O(N2),最好只有 O ( N ) O(N) O(N),那么一个直接的思路就是使用DSU(这部分内容的说明可以参考我们之前的博客:Python笔记:并查集(DSU)结构简介)。 到这里事实上比赛的时候我们也想到了,但是具体的实现上却犯了难,因为他事实上只允许使用有限的边,是要返回两个点的所有连通方案中的最小实现方案中的最大边长,而DSU只能够反应聚合关系以及整个聚类的一些特征,而无法反应其中更小的集合的关系,因此比赛的时候就没有想出来,当然,这个后续可以通过其他方式绕开,我们等会再说。 然后另外一种思路就是使用动态规划,将历史计算结果全部保存在缓存当中,从而优化执行效果,这个是我比赛的时候的思路,但是遇到的问题就是怎么存储状态,然后你懂的,不到南墙不回头,死的不能更惨,唉。。。 我们回过头来重新说说正确的使用dsu的方法吧。 问题如果简化到对于某一个具体的query,事实上我们只要使用边长小于limit的边进行dsu的构建即可。因此,我们就可以很简单的使用dsu进行解答,但是,问题在于对于每一个query,我们都要针对limit进行一次dsu的构建,这个就会非常复杂,必然会带来超时问题。 但是,如果我们实现对query针对limit进行排序的话,那么,我们事实上就不需要每次都进行dsu的构建了,只要全局的构建一个dsu结构就能够解决这个问题了。 2. 代码实现我们给出具体的python代码实现如下: 代码语言:javascript复制class DSU: def __init__(self, n): self.dsu = [i for i in range(n)] def find(self, x): if self.dsu[x] != x: self.dsu[x] = self.find(self.dsu[x]) return self.dsu[x] def union(self, x, y): x = self.find(x) y = self.find(y) if x != y: self.dsu[y] = x return class Solution: def distanceLimitedPathsExist(self, n: int, edgeList: List[List[int]], queries: List[List[int]]) -> List[bool]: edges = sorted(edgeList, key=lambda x: x[2]) queries = sorted([(idx, u, v, limit) for idx, (u, v, limit) in enumerate(queries)], key=lambda x: x[-1]) m = len(edges) res = [False for _ in queries] i = 0 dsu = DSU(n) for idx, u, v, limit in queries: while i < m and edges[i][2] < limit: p, q, _ = edges[i] dsu.union(p, q) i += 1 res[idx] = (dsu.find(u) == dsu.find(v)) return res提交代码评测得到:耗时2028ms,占用内存61.2MB。 当前最优的代码实现耗时1812ms,但是看了一下同样是采用dsu方式进行的实现,因此这里就不过多进行展开了。