赞
踩
这次的比赛结果略惨,只做出3题,然后国内才只有229/3690,世界则是667/9606,都是只有前7%的水平。
但是这次的题目应该算是比较简单的,看了一下,第一名的大佬只花了不到10分钟就搞定了,而我没有做出来的最后一题,事实上就是一个DSU的问题,之前我也专门写博客聊过这个算法(Python笔记:并查集(DSU)结构简介),然而在比赛中还是没有搞出来。
倒不是完全没有想到过使用DSU,但是终究还是想岔了,没能想到正确的使用方法,导致最后一题没有做出来,不过这个后面再谈吧,唉。。。
给出题目一的试题链接如下:
这一题挺直接的,解题思路上就是先去掉空格和-
字符,然后按照3个数字为一组进行分割,知道最后几位进行特殊处理即可。
给出python代码实现如下:
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,但看了一下思路是一致的,因此这里就不过多展开了。
给出题目二的试题链接如下:
这一题的思路其实还是蛮明确的,就是维护一个滑动窗口,确保窗口中不会存在重复的元素。
给出python代码实现如下:
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,但是看了一下,他们的算法思路和我们是相同的,因此这里就不过多展开了。
给出题目三的试题链接如下:
这一题的思路就是使用动态规划算法。
他的递推关系还是非常明确的,可以给出递推关系如下:
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<j≤i+k)
我们尝试了直接暴力地求解,但是这样的时间复杂度是 O ( N × K ) O(N \times K) O(N×K)的,就会导致超时,但是这一题事实上可以参考上一周双周赛的最后一题的思路,使用一个有序队列进行处理,就可以将时间复杂度退化到 O ( N ) O(N) O(N),具体的算法说明可以参考我们上周的比赛记录(LeetCode笔记:Biweekly Contest 41 比赛记录),这里就不对具体的细节进行展开了。
我们直接给出python代码实现如下:
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,但是看了一下他们的思路和我们是完全一致的,因此这里就不多做展开了。
给出题目四的试题链接如下:
这题比赛的时候没做出来真的是太伤了,赛后看了一下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结构就能够解决这个问题了。
我们给出具体的python代码实现如下:
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方式进行的实现,因此这里就不过多进行展开了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。