LeetCode笔记:Weekly Contest 217 比赛记录

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

0. 赛后总结

唉,昨天刚打了一场漂亮的翻身仗,为上周遭受的不公正待遇小小地报了个仇,结果今天就惨遭滑铁卢,硬生生地只做出来两道题,而且第二题还连错了三次。

然后就只剩下国内579,世界1501了,前15%都没能进去,只能勉强卡进前20%,真的是实名的惨。。。

虽说第二题的三次错里面有两次完全是因为心态问题,但是调整心态的能力也是实力的一环不是吗?有句话怎么说来着,所有的悲剧都是由于当事人本身的能力不足导致的。。。

唉,幻想终究有被打破的一刻,认清自己的弱小吧,然后挣扎着向上爬吧,骚年。。。

道阻且长,吾将上下而求索。。。

1. 题目一

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

  • 5613. 最富有客户的资产总量

1. 解题思路

这一题的解题思路非常地直白,是这次的题目的当中最简单的一题了,只要对每一行进行一下求和,然后求取最大值即可。

2. 代码实现

给出python代码实现如下:

class Solution:
    def maximumWealth(self, accounts: List[List[int]]) -> int:
        return max([sum(x) for x in accounts])

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

当前最优的代码实现耗时52ms,但是思路是完全一样的,只有实现细节上略有差别。

2. 题目二

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

  • 5614. 找出最具竞争力的子序列

1. 解题思路

这道题思路其实挺直接的,就是首先找到前n-k个数当中最小的数作为第一个数,记其位置为idx,然后找从idx+1到第n-k+1最小的数作为第二个数,重复上述操作直到所有的数填完即可。

为了优化代码效率,我们使用heapq库进行优化代码实现。

2. 代码实现

给出python代码实现如下:

class Solution:
    def mostCompetitive(self, nums: List[int], k: int) -> List[int]:
        n = len(nums)
        q = [(x, idx) for idx, x in enumerate(nums[:-k])]
        heapq.heapify(q)
        res = [0 for _ in range(k)]
        pre = -1
        for i in range(k):
            heapq.heappush(q, (nums[n-k+i], n-k+i))
            while q:
                x, idx = heapq.heappop(q)
                if idx > pre:
                    res[i] = x
                    pre = idx
                    break
        return res

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

当前最优的代码实现耗时仅852ms,实测之后发现其运行耗时1260ms,执行效率上较之我们有所提升,不过并没有一倍这么夸张。

不幸的是,看了一下他们的代码,横竖没能看懂他们的代码逻辑,也没能理解他们的性能优化点在哪里,有兴趣的读者可以自行去看一下,这里就不做展开了,因为确实没有看懂,唉。。。

其他倒是有另外一个解题的思路,算法复杂度理论上只有 O ( N ) O(N) O(N),然而奇葩的是,实际运行之后却发现执行效率反而变低了,真的是理解不能。。。

3. 题目三

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

  • 5615. 使数组互补的最少操作次数

1. 解题思路

这一题我们最开始的思路是通过统计当前和的频率来考察应该选择那个数作为求和目标,通过这样的方式也能减少一定的遍历复杂度,但是还是不可避免地遇到了超时问题,直到最后也没能将超时问题解决。

比赛结束之后,看了一下大佬们的解题思路,发现他们的思路非常巧妙。

本质上来说,他们是统计每一个生成范围内的每一个数作为目标互补数时需要经历的变换次数,这个思路我们最开始也想过,不过后来处于算法复杂度的想法放弃了,不过大佬们针对这个进行了一个极其优秀的优化。

他们在一轮统计中只考虑几个关键节点,假设两个数从小到大分别为 x x x y y y

  1. 2 2 2 x x x范围内,要变成这些数,需要经过的变换次数为2;
  2. x x x x + y − 1 x+y-1 x+y1范围内,变换的次数为1;
  3. 对于 x + y x+y x+y,不需要经过额外的变换操作,即变换次数为0;
  4. x + y + 1 x+y+1 x+y+1 y + l i m i t y+limit y+limit范围内,变换次数为1;
  5. 对于 y + l i m i t + 1 y+limit+1 y+limit+1或以上的目标,需要的变换次数为2;

因此,事实上我们只要在关键节点上记录变动次数,然后做一个累加求和就可以了。

事实上,这种解法我之前绝对就用过,可惜没能联想到这里,导致这道题没能做出来,果然还是做不到看山是山,看水是水的境界啊。

2. 代码实现

给出python代码实现如下:

class Solution:
    def minMoves(self, nums: List[int], limit: int) -> int:
        counter = [0] * (limit*2+2)
        n = len(nums)
        for i in range(n // 2):
            x, y = sorted([nums[i], nums[n-1-i]])
            counter[2] += 2
            counter[x+1] -= 1
            counter[x+y] -= 1
            counter[x+y+1] += 1
            counter[y+limit+1] +=1
        counter = list(accumulate(counter))
        return min(counter[2:-1])

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

当前最优的代码实现耗时1080ms,但是本质上来说和上述解题思路是完全一样的。

4. 题目四

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

  • 5616. 数组的最小偏移量

1. 解题思路

这一题比赛的时候思路就混乱的一塌糊涂,做了半天,各种问题。

赛后看了一下大佬们的解答,发现代码意外的简洁,都有点惊呆了,然后细看了一下之后,发现答案意外的简单。

首先,我们将所有的数全部变换为偶数,这么一来,之后对所有的数我们都只能进行除法操作,换言之,所有的数都只能变小,不能再变大了,因此,我们只需要不停地取出最大的元素,如果最大的元素能被二整除,那么我们就将其除以2,考察新的数组中最大的元素与最小的元素之间的差值,取其中的最小值即可。

2. 代码实现

给出python代码实现如下:

class Solution:
    def minimumDeviation(self, nums: List[int]) -> int:
        for i, x in enumerate(nums):
            if x % 2 == 1:
                nums[i] = x * 2
        nums = sorted(nums)

        ans = nums[-1] - nums[0]
        while nums[-1] % 2 == 0:
            x = nums.pop()
            x = x // 2
            bisect.insort(nums, x)
            ans = min(ans, nums[-1] - nums[0])
        return ans

提交上述代码评测得到:耗时3860ms,占用内存26.4MB。

然而当前最优的代码实现耗时仅720ms,较之我们的算法有近5倍的性能提升,因此,我们需要研究一下他们的算法思路。

3. 代码优化

仔细看了一下当前最优的代码实现,发现和我们的思路几乎是一模一样的,唯一的区别在于他们不是使用二分插入的方式维护一个有序数组,而是使用heapq维护一个堆。

于是我们也换用堆的方式继续了代码实现:

class Solution:
    def minimumDeviation(self, nums: List[int]) -> int:
        for i, x in enumerate(nums):
            if x % 2 == 1:
                nums[i] = -x * 2
            else:
                nums[i] = -x
        _max = max(nums)
        
        heapq.heapify(nums)
        ans = _max - nums[0]
        while nums[0] % 2 == 0:
            x = heapq.heappop(nums)
            x = x // 2
            _max = max(x, _max)
            heapq.heappush(nums, x)
            ans = min(ans, _max - nums[0])
        return ans

提交代码之后发现:耗时940ms,占用内存26.5MB,与当前最优的代码实现已经相差无几。。。

就纳闷了,原则上来说大家的算法复杂度都是 O ( N ⋅ l o g N ) O(N \cdot logN) O(NlogN啊,为啥使用堆排序效率上救恩那个提升辣么多啊?!

唉,表示已经放弃了思考,如果有那位读者能够想明白这个问题,请务必在评论区指点一下迷津,感激不尽!

你可能感兴趣的:(leetcode笔记,算法,leetcode,python)