LeetCode笔记:Weekly Contest 222 比赛记录

  • LeetCode笔记:Weekly Contest 222
    • 0. 赛后总结
    • 1. 题目一
      • 1. 解题思路
      • 2. 代码实现
    • 2. 题目二
      • 1. 解题思路
      • 2. 代码实现
    • 3. 题目三
      • 1. 解题思路
      • 2. 代码实现
    • 4. 题目四
      • 1. 解题思路
      • 2. 代码实现

0. 赛后总结

又是一个悲凉的早上,又是刷新下限的一次比赛,好久没有那么绝望过了,只做出两题,甚至一度差点以为只能做出一题,最终排名国内632/3117,世界排名1835/9692,差不多就是前20%的水平,真的是,一度回想起刚开始打比赛时候的绝望。。。

唉,感觉最近诸事不顺,明明悲惨的2020已经过去了,为啥一切都还是如此呢,唉。。。

或许我真的需要休息一阵子了,希望过年的时候能够好好休息一下吧。

1. 题目一

给出题目一的试题链接如下:

  • 5641. 卡车上的最大单元数

1. 解题思路

这题的思路感觉没啥,就是照着做就是了,先按照每个箱子的装载能力进行排序,然后选择装载能力最强的箱子进行装载就是了。

2. 代码实现

给出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. 题目二

给出题目二的试题链接如下:

  • 5642. 大餐计数

1. 解题思路

坦率地说,这一题事实上也不难,如果不用特别考虑时间复杂度的话,那么就用一个字典来存储不同美味度的菜品数目,然后遍历一下加起来恰好等于 2 n 2^n 2n的其他菜品,然后求个和即可。

只是这道题比较坑,给的测试样例有隐藏,然后就只能猜问题在哪里,结果就是下方 2 0 2^0 20没考察,然后上面还漏了一个 2 21 2^{21} 221,然后就呵呵了。

唉,累感不爱。。。

2. 代码实现

给出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。

同样由于当前没有足够的提交代码,所以暂时不知道是否存在更好的算法实现。

3. 题目三

给出题目三的试题链接如下:

  • 5643. 将数组分成三个子数组的方案数

1. 解题思路

这道题的思路坦率来说并不很难,本质上来说就是累计求和之后然后给出在每一个第一分割点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 sisjsisnsj

进而可以得到:
{ 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. sjsj2×si2sn+si

如此一来,我们就可以看到,右界k事实上也是单调递增的。

从而,我们就可以通过一次遍历直接求得最终的答案。

2. 代码实现

给出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。

4. 题目四

给出题目四的试题链接如下:

  • 5644. 得到子序列的最少操作次数

1. 解题思路

这一题最为暴力的一个思路就是直接求解最长公共子序列,然后看一下target与之的差值即为最终的答案。

有关最长公共子序列的求发可以详见我之前的博客:NLP笔记:浅谈字符串之间的距离。

但是,lcs算法的算法复杂度是 O ( N 2 ) O(N^2) O(N2),对于这道题,我们会遇到超时问题。

后续看了一下leetcode上面的一些解法,发现:

  • 主体思路还是基于lcs进行优化,由于target各个数字均不相同,因此,可以将其优化为求最大递增子序列的方式,从而将时间复杂度降至 O ( N ⋅ l o g N ) O(N\cdot logN) O(NlogN)

有关最长最大子序列的求解方法,可以详见leetcode官方的解答说明:

  • https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/zui-chang-shang-sheng-zi-xu-lie-by-leetcode-soluti/

其核心点在于,维护一个数组d,使得d中的每一个元素d[i]都表示当最长递增子序列长为 i + 1 i+1 i+1时,尾部元素最小的情况。

此时显然有推论:

  • 数组d是一个单调递增的序列。

因此,我们就可以采用二分法对该数组进行维护,从而优化算法的执行效率,将整体的执行效率从 O ( N 2 ) O(N^2) O(N2)降维至 O ( N ⋅ l o g N ) O(N \cdot logN) O(NlogN)

2. 代码实现

给出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。

属于当前的最优代码实现。

你可能感兴趣的:(leetcode笔记,算法,leetcode,python,最大递增子序列)