LeetCode笔记:Biweekly Contest 35 比赛记录

0. 赛后总结

中文的leetcode果然和我犯冲,这次比赛又是桑心的一逼,题目倒还好,虽然第二第三题卡了一会,但是好歹都做出来了,但是最坑的是第四题居然比赛的时候没做出来,然后比赛结束之后10分钟给搞定了。。。10分钟,就差这10分钟。。。

唉,见鬼了。

整体排名不算差吧,国内前200,世界500多点,但是考虑到双周赛参加的人本来就少,这个成绩就很尴尬了,更何况我就差那么10分钟。。。

算了,不口吐芬芳了,毕竟一晚上过去,也多少看淡了一点,好好梳理一下,然后再接再厉吧。。。

≧ ﹏ ≦

1. 题目一

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

1. 解题思路

第一题的题目算是中规中矩吧,目标是计算奇数长度的连续子序列的总和的总和。

因此,我们只需要进行一个二重循环遍历就行了。

不过,为了更好地提升代码效率,我们可以求出累积数组,而后序列的求和操作就可以直接换算为一个减法操作就可以了,可以省掉一层循环。

2. 代码实现

给出代码实现如下:

class Solution:
    def sumOddLengthSubarrays(self, arr: List[int]) -> int:
        cumsum = [0]
        for n in arr:
            cumsum.append(cumsum[-1] + n)
        n = len(arr)
        ans = 0
        for i in range(n):
            for j in range(i+1, n+1,2):
                s = cumsum[j] - cumsum[i]
                ans += s
        return ans

提交代码评测得到:耗时40ms,占用内存14MB。

当前最优解耗时32ms,但是看了一下算法思路本身乃至实现都是几乎完全一样的,因此就不再过多赘述了。

2. 题目二

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

1. 解题思路

这一题的算法思路本身还是蛮清晰的,其实就是看requests中所有请求之间的重合部分,并按照重合次数对idx进行排序,然后分别从大到小向其中填充入原数组中的元素即可。

但是,比较难的一点在于如何去实现这个算法,要计算重合次数本身并不是一件很轻松的事。

后来倒是想到了一种个人认为比较巧妙的方法,就是对于每一个requests,在首部+1,尾部-1,而后求整个数组的累积和,这样每个idx中的累积和就是这个idx在requests中被重复请求的次数。

从而,我们只需要对idx进行排序,再对数组进行排序,然后对应元素相乘求和即可得到我们最终的答案。

2. 代码实现

给出python代码实现如下:

class Solution:
    def maxSumRangeQuery(self, nums: List[int], requests: List[List[int]]) -> int:
        MOD = 1000000007
        n = len(nums)
        counter = [0 for i in range(n+1)]
        for st, ed in requests:
            counter[st] += 1
            counter[ed+1] -= 1
        for i in range(1, n+1):
            counter[i] += counter[i-1]
        counter = sorted(counter[:-1])
        nums = sorted(nums)
        ans = 0
        for i in range(n):
            ans = (ans + counter[i] * nums[i]) % MOD
        return ans

提交代码评测得到:耗时1664ms,占用内存46.9MB。

当前的最优算法耗时1388ms,但是看了一下其算法本身和我们的算法是完全一致的,唯一不同的就是最后的求和过程他用了一个sum函数。

因此,这里也就不在多做赘述了。

3. 题目三

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

1. 解题思路

第三题我在比赛的时候倒是超时了两次,不过结果发现一次是因为读题失误,一次则是因为数据处理上的问题,思路上倒是一直没啥大的问题,所以后来就直接改对了,算是可喜可贺吧,呵呵。

这一题的解题思路其实也是蛮清晰的,要使得整个数组之和最终能够被p整除,那么我们要做的首先就是求出总的数组之和对p得余数,假设为r,如果r为0,说明本身就能够被整除,那么直接返回0即可;反之,就是去找其中每一个元素k,假设sum[:k+1] % p = r',那么需要删除的最短序列长度就是下一个使得sum[:k+l] % p = r' + r的数组长度l。

剩下的问题就是代码实现了。

2. 代码实现

我们给出上述思路的一种python实现算法如下:

import math
import collections

class Solution:
    def minSubarray(self, nums: List[int], p: int) -> int:        
        r = 0
        counter = collections.defaultdict(list)
        counter[0] = [0]
        for idx, n in enumerate(nums):
            r = (r + n) % p
            counter[r].append(idx+1)
        if r == 0:
            return 0
        
        @lru_cache(None)
        def get_min(r1, r2):
            s1 = counter.get(r1, [])
            s2 = counter.get(r2, [])
            n1 = len(s1)
            n2 = len(s2)
            i = 0
            j = 0
            ans = math.inf
            while i < n1:
                while j < n2 and s2[j] < s1[i]:
                    j += 1
                if j == n2:
                    break
                while i < n1 and s1[i] < s2[j]:
                    i += 1
                ans = min(ans, s2[j]-s1[i-1])
            return ans
        
        ans = math.inf
        for i in counter.keys():
            tgt = (i + r) % p
            ans = min(ans, get_min(i, tgt))
        return ans if ans != len(nums) else -1

提交代码评测得到:耗时916ms,占用内存58.5MB。

当前的最优算法耗时仅576ms,差不多有一倍的差距,说明上述实现肯定有什么改进的地方。

3. 算法优化

看了一下当前最优的解法,发现思路是一致的,但是他们的统计是计算最近一个和为r'-r的元素,这样就可以少做一次遍历,优化了时间复杂度。

给出他们的代码实现如下:

class Solution:
    def minSubarray(self, nums: List[int], p: int) -> int:     
        n = len(nums)
        r = sum(nums) % p
        if r == 0:
            return 0
        counter = {
    
    0: 0}
        ans = n
        s = 0
        for idx, k in enumerate(nums):
            s = (s + k) % p
            tgt = (s - r + p) % p
            if tgt in counter:
                ans = min(ans, idx+1 - counter[tgt])
            counter[s] = idx+1
            # print(counter, ans)
        # print(counter, ans)
        return ans if ans != n else -1

提交代码评测得到:耗时544ms,占用内存32.2MB。为当前最有方案。

4. 题目四

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

1. 解题思路

这一题真的可惜了,比赛的时候没能解出来,但实际上这一题并没有很难,思路对了的话花不了太多的时间。

这一题真的可惜了,比赛的时候没能解出来,但实际上这一题并没有很难,思路对了的话花不了太多的时间。

事实上,这一题的思路就是反向画回去,从最顶层的图层开始绘制,一直绘制到不能绘制为止。

首先,我们找到每一种颜色的原始绘制区域,然后,看他是否属于当前的最顶层图层,如果是,那么将其上色,反之就先等等。如果能够一直绘制到底,将所有的颜色都涂上,那么就说明可以实现,反之就说明不能绘制

2. 代码实现

给出代码实现如下:

class Solution:
    def isPrintable(self, grid: List[List[int]]) -> bool:
        n = len(grid)
        m = len(grid[0])
        colors = {
    
    }
        for i in range(n):
            for j in range(m):
                if grid[i][j] not in colors:
                    colors[grid[i][j]] = [i, j, i, j]
                else:
                    colors[grid[i][j]] = [min(colors[grid[i][j]][0], i), min(colors[grid[i][j]][1], j), max(colors[grid[i][j]][2], i), max(colors[grid[i][j]][3], j)]
        colors = colors.items()
        
        painted = [[0 for _ in range(m)] for _ in range(n)]
        
        def is_paintable(block):
            c, (x1, y1, x2, y2) = block
            for i in range(x1, x2+1):
                for j in range(y1, y2+1):
                    if painted[i][j] == 0 and grid[i][j] != c:
                        return False
            return True
        def paint(blocks):
            nonlocal painted
            c, (x1, y1, x2, y2) = block
            for i in range(x1, x2+1):
                for j in range(y1, y2+1):
                    if painted[i][j] == 0:
                        painted[i][j] = c
            return
        
        flag = True
        while flag and colors != []:
            tmp = []
            flag = False
            for block in colors:
                if is_paintable(block):
                    paint(colors)
                    flag = True
                else:
                    tmp.append(block)
            colors = tmp
        
        return flag

提交代码评测得到:耗时348ms,占用内存13.9MB。

当前最优算法耗时224ms,差了差不多一半的时间,如果有兴趣的读者可以自行去看一下,这里我就暂时不做什么研究了。。。

猜你喜欢

转载自blog.csdn.net/codename_cys/article/details/108692744
今日推荐