又是一个悲凉的早上,又是刷新下限的一次比赛,好久没有那么绝望过了,只做出两题,甚至一度差点以为只能做出一题,最终排名国内632/3117,世界排名1835/9692,差不多就是前20%的水平,真的是,一度回想起刚开始打比赛时候的绝望。。。
唉,感觉最近诸事不顺,明明悲惨的2020已经过去了,为啥一切都还是如此呢,唉。。。
或许我真的需要休息一阵子了,希望过年的时候能够好好休息一下吧。
给出题目一的试题链接如下:
这题的思路感觉没啥,就是照着做就是了,先按照每个箱子的装载能力进行排序,然后选择装载能力最强的箱子进行装载就是了。
给出python代码实现如下:
class Solution:
def maximumUnits(self, boxTypes: List[List[int]], truckSize: int) -> int:
boxTypes = sorted(boxTypes, key=lambda x: x[1], reverse=True)
res = 0
n = len(boxTypes)
i = 0
while i<n and truckSize > 0:
if boxTypes[i][0] <= truckSize:
res += boxTypes[i][0] * boxTypes[i][1]
truckSize -= boxTypes[i][0]
else:
res += truckSize * boxTypes[i][1]
truckSize = 0
i += 1
return res
提交代码评测得到:耗时148ms,占用内存14.8MB。
当前还没有更多的代码提交样例,因此暂时不知道是否有更好的算法实现方法,不过估计是大差不差了。
给出题目二的试题链接如下:
坦率地说,这一题事实上也不难,如果不用特别考虑时间复杂度的话,那么就用一个字典来存储不同美味度的菜品数目,然后遍历一下加起来恰好等于 2 n 2^n 2n的其他菜品,然后求个和即可。
只是这道题比较坑,给的测试样例有隐藏,然后就只能猜问题在哪里,结果就是下方 2 0 2^0 20没考察,然后上面还漏了一个 2 21 2^{21} 221,然后就呵呵了。
唉,累感不爱。。。
给出python代码实现如下:
class Solution:
def countPairs(self, deliciousness: List[int]) -> int:
counter = Counter(deliciousness)
elems = sorted(counter.keys())
ans = 0
s = 1
for _ in range(22):
for i in elems:
if i > s // 2:
break
elif i == s / 2:
ans += (counter[i] * (counter[i]-1)) // 2
break
elif s-i in counter:
ans += counter[i] * counter[s-i]
ans = ans % 1000000007
s *= 2
return ans
提交代码评测得到:耗时692ms,占用内存20.6MB。
同样由于当前没有足够的提交代码,所以暂时不知道是否存在更好的算法实现。
给出题目三的试题链接如下:
这道题的思路坦率来说并不很难,本质上来说就是累计求和之后然后给出在每一个第一分割点i给定的情况下mid的范围的最小值j和最大值k,然后累加一下即可。
显然,i和j都是单调的,但是k比赛的时候我没有想清楚,不确定是否一定单调,就又给了一个循环,结果就超时了,后来又考率各种边界条件,总之死活就是搞不定。
后来比赛结束之后仔细想了一下,发现递推关系事实上还是蛮清晰的,很简单就能看到,当分成三段之后,有:
s i ≤ s j − s i ≤ s n − s j s_i \leq s_j - s_i \leq s_n-s_j si≤sj−si≤sn−sj
进而可以得到:
{ s j ≥ 2 × s i s j ≤ s n + s i 2 \left\{ \begin{aligned} s_j &\geq 2 \times s_i \\ s_j &\leq \frac{s_n + s_i}{2} \end{aligned} \right. ⎩⎨⎧sjsj≥2×si≤2sn+si
如此一来,我们就可以看到,右界k事实上也是单调递增的。
从而,我们就可以通过一次遍历直接求得最终的答案。
给出python代码实现如下:
class Solution:
def waysToSplit(self, nums: List[int]) -> int:
MOD = 10**9+7
s = [0] + list(accumulate(nums))
n = len(nums)
i, j, k = 1, 2, 2
ans = 0
while i < n:
j = max(i+1, j)
while j < n and s[j] < 2*s[i]:
j += 1
if s[i] > s[n] / 3:
break
k = max(j, k)
while k < n and s[k] <= 0.5 * (s[-1]+s[i]):
k += 1
if k > j:
ans = (ans + k-j) % MOD
i += 1
return ans
提交代码评测得到:耗时1048ms,占用内存27.1MB。
给出题目四的试题链接如下:
这一题最为暴力的一个思路就是直接求解最长公共子序列,然后看一下target与之的差值即为最终的答案。
有关最长公共子序列的求发可以详见我之前的博客:NLP笔记:浅谈字符串之间的距离。
但是,lcs算法的算法复杂度是 O ( N 2 ) O(N^2) O(N2),对于这道题,我们会遇到超时问题。
后续看了一下leetcode上面的一些解法,发现:
有关最长最大子序列的求解方法,可以详见leetcode官方的解答说明:
其核心点在于,维护一个数组d,使得d中的每一个元素d[i]
都表示当最长递增子序列长为 i + 1 i+1 i+1时,尾部元素最小的情况。
此时显然有推论:
因此,我们就可以采用二分法对该数组进行维护,从而优化算法的执行效率,将整体的执行效率从 O ( N 2 ) O(N^2) O(N2)降维至 O ( N ⋅ l o g N ) O(N \cdot logN) O(N⋅logN)。
给出python的代码实现如下:
class Solution:
def minOperations(self, target: List[int], arr: List[int]) -> int:
mapping = {
v: idx for idx, v in enumerate(target)}
arr = [mapping[x] for x in arr if x in mapping]
n = len(arr)
def lis(arr):
d = []
for x in arr:
if d == [] or d[-1] < x:
d.append(x)
else:
idx = bisect.bisect_right(d, x)
if d[idx-1] != x:
d[idx] = x
return len(d)
return len(target) - lis(arr)
提交代码评测得到:耗时984ms,占用内存37.5MB。
属于当前的最优代码实现。