LeetCode笔记:Biweekly Contest 34 比赛记录

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

0. 赛后总结

不知道是不是错觉,双周赛感觉是要比周赛多少简单一点,这一次的题目又是全部搞定了,总共耗时52分钟,不过错了两次,因此最终的成绩在1h,很可惜的刚好跑出了全国前100(102名),全球的成绩则为268名,除了上上周那次前两百的成绩,这次算是最好的一次战绩了。

加油,再接再厉,再接再厉,争取有一天可以把成绩稳定到这个水平!

1. 题目一

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

  • 5491. 矩阵对角线元素的和

1. 解题思路

这一题的思路没啥好多说的,就是暴力的遍历一下两条对角线然后求一下和就行了,对于 N × N N \times N N×N的矩阵,时间复杂度就是 O ( N ) O(N) O(N),唯一需要注意一下的就是当N为奇数时,两条对角线都会经过中点,需要去一下重。

2. 代码实现

给出python代码实现如下:

class Solution:
    def diagonalSum(self, mat: List[List[int]]) -> int:
        n = len(mat)
        ans = 0
        for i in range(n):
            ans += mat[i][i] + mat[i][n-1-i]
            if i == n-1-i:
                ans -= mat[i][i]
        return ans

提交代码后评测得到:耗时116ms,占用内存13.8MB。虽然不是当前的最优解,但是完全是实现细节上有所差别,这里就不再赘述了。

2. 题目二

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

  • 5492. 分割字符串的方案数

1. 解题思路

这一题事实上也还好,就是统计有多少个1:

  1. 如果1的个数无法被三整除,则无法切分,返回-1;
  2. 如果1的个数为0,则所有元素都为0,那么就是将所有的元素分为三堆,假设总共有N个元素,则可能的切分方法数量为 C N − 1 2 C_{N-1}^{2} CN12
  3. 如果1的个数为 3 k 3k 3k个,则第一刀切在第 k + 1 k+1 k+1个1之前,第二刀切在第 2 k + 1 2k+1 2k+1个1之前,假设两者之前分别有n和m个0,则可能的切分方法为 ( n + 1 ) × ( m + 1 ) (n+1) \times (m+1) (n+1)×(m+1)

2. 代码实现

我们将上述代码思路转换为python代码即有:

class Solution:
    def numWays(self, s: str) -> int:
        MOD = 1000000007
        one_count = 0
        zero_count = 0
        cache = {
     }
        for c in s:
            if c == '0':
                zero_count += 1
            else:
                one_count += 1
                cache[one_count] = zero_count
                zero_count = 0
        if one_count not in cache.keys():
            cache[one_count] = zero_count
        if one_count % 3 != 0:
            return 0
        elif one_count == 0:
            return (zero_count-2) * (zero_count-1) // 2 % MOD
        else:
            return (cache[one_count // 3 +1] + 1) * (cache[one_count // 3 * 2 +1] + 1) % MOD

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

emmmm,不用吐槽这里的变量命名,我懂得,但是实在没想到什么好的命名方法,然后比赛的时候又赶时间,然后就变成这副德行了。。。(。>︿<)_θ

另外当前的最优解耗时88ms,但是看了一下,思路是完全相同的,所以这里也就不再多做赘述了。

3. 题目三

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

  • 5493. 删除最短的子数组使剩余数组有序

1. 解题思路

这一题算是我这次比赛里面做的最失败的一题了,先是题目理解错误,再是又出现了一次边界条件没想清楚,结果就呵呵了。

这题最需要注意的是,他这里的删除的子串是一段连续的子串,而不是可以任意选择每个元素是否进行删除,如果是后者的话就要用迭代的方式进行考察了,但是这里既然要求只能删除连续的一串子串,那么事实上题目就被大大简化了。

在只能删除一串子串的情况下,保留下来的子序列就一定是一段头部的序列加上一段尾部的序列(两者可以为空),因此,我们只要找到头部的最长连续非减子串以及尾部的最长连续非减子串,然后将两者进行合并即可

我们可以将上述思路进一步细化到下述解题步骤:

  1. 如果整个序列就是非减的,那么直接返回0就可以了;
  2. 找到头部和尾部的最长非减连续子串,记为s1和s2;
  3. 合并s1和s2,使得s1与s2保持非减的情况下拥有最大的长度;

此时,总长度减去保留下来的子串的长度就是我们最少需要删除的序列长度了。

2. 代码实现

给出上述思路下的代码实现如下:

class Solution:
    def findLengthOfShortestSubarray(self, arr: List[int]) -> int:
        n = len(arr)
        if n == 1:
            return 0
        st_last = n-1
        for i in range(n-1):
            if arr[i+1] < arr[i]:
                st_last = i
                break
        if st_last == n-1:
            return 0
        ed_first = 0
        for i in range(n-1, 0, -1):
            if arr[i-1] > arr[i]:
                ed_first = i
                break
                
        @lru_cache(None)
        def merge(st_last, ed_first):
            if st_last == -1 or ed_first == n or arr[st_last] <= arr[ed_first]:
                return st_last + 1 + n - ed_first
            else:
                return max(merge(st_last-1, ed_first), merge(st_last, ed_first+1))
            
        left_len = merge(st_last, ed_first)
        return n - left_len

提交代码之后评测得到:耗时640ms,占用内存42.6MB。为当前的最优解法。

4. 题目四

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

  • 5494. 统计所有可行路径

1. 解题思路

虽然这一题的难度划分是hard,但是较之上一题,这一题的解题思路事实上会更加清晰一些。

我们只需要一直走,直到走到没油为止即可。

可以同一个递归的思路来解这道题:

  1. 如果当前汽油已经为负了,说明走不到终点,可以停止计算;
  2. 如果当前城市为目标城市,那么总的路线数目+1;
  3. 如果当前汽油还有,那么继续从当前城市出发到下一个城市,累加他们当中经过目标城市的路径总数即为最终的答案。

需要注意的是,直接这么迭代一定会出现超时的问题,需要使用动态规划的方式用空间换取时间。

不过,这里,我们就采用python的缓存机制来直接暴力求解了。

2. 代码实现

给出python代码实现如下:

class Solution:
    def countRoutes(self, locations: List[int], start: int, finish: int, fuel: int) -> int:
        MOD = 1000000007
        fuel_cost = [[abs(i - j) for j in locations] for i in locations]
        n = len(locations)
        
        @lru_cache(None)
        def dp(loc, fuel):
            if fuel < 0:
                return 0 
            ans = 1 if loc == finish else 0
            for i in range(n):
                if i == loc:
                    continue
                ans += dp(i, fuel-fuel_cost[loc][i])
            return ans % MOD
        
        return dp(start, fuel)

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

当前最优算法耗时360ms,看了一下,他的算法思路和我本质上是一样的,但是他就是使用了一般的动态规划方式,构建了一个 N × ( f u e l + 1 ) N \times (fuel+1) N×(fuel+1)的矩阵来保存中间计算结果。

如果感兴趣的读者可以自行研究一下,毕竟实话实说,使用缓存虽然实现起来方便很多,但一直觉得是邪道来着。。。>﹏<

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