这一次的leetcode比赛题目依然相对比较简单,从大佬们的解题时间就可以看出一二(前五名都在20分钟内搞定,最快的uwi大佬只用了12分钟。。。)。
比较开心的是,这一次我也把四道题全部搞定了,整体时间上花了1小时15分钟多点,不过错了两次,一次超时,一次题目没看仔细。。。但是整体来说,这个成绩还是让我比较满意的。
おめでとう,おめでとう!
然后就解题感受来说,第一题easy的题目依然中规中矩,没啥好说的,倒是第二第三题卡了我一会,这两题的灵感来源感觉都是冒泡排序,不过解题的时候思路并没有很顺畅,尤其是第二题,卡了我好一会儿,所幸最后还是搞定了,倒是最后一道hard的题目,倒是一道非常常见的动态规划题目,反而没有花费我太多的时间。
下面,我们还是来给出各个题目的详细解题过程。
给出题目一的试题链接如下:
这一题本身还是比较复杂的,需要比对所有的三元组 ( i , j , k ) (i,j,k) (i,j,k)是否满足题目定义的good triplet的条件。
但是由于题目本身限制了array的长度不会超过100,因此,我们可以直接采用三重循环方式的方式直接暴力求解,即遍历所有可能的三元组,看其是否满足good triplet条件。
给出其代码实现如下:
class Solution:
def countGoodTriplets(self, arr: List[int], a: int, b: int, c: int) -> int:
def is_good(i, j, k):
return abs(arr[i]-arr[j]) <= a and abs(arr[j]-arr[k]) <= b and abs(arr[i]-arr[k]) <= c
n = len(arr)
ans = 0
for i in range(n-2):
for j in range(i+1, n-1):
for k in range(j+1, n):
if is_good(i, j, k):
ans += 1
return ans
提交代码后评测得到:代码耗时1524ms,占用内存13.8MB。
而现有的最优解答耗时676ms。但是考察其代码发现其实现方式与我们没有本质区别,差别在于我们定义的is_good()
函数损伤了代码性能。
给出题目二的试题链接如下:
这一题坦率地说比赛的时候卡了我有一阵子,因为想不清楚多次比较之后数组的变化。当然,我们可以暴力的通过不停地pop和append的操作复现题目的操作过程,但是由于数组长度n和目标连续胜场次数k都可能非常大,因此担心暴力求解会导致超时。
因此,比较合理的想法是考虑其具体的过程然后找出规律,从而找到一个更为优雅直接的解题思路。(不过后期来看还是没能避免超时报错的命运)
现在,我们来考虑题目中描述的整体过程。
首先,如果给出的连胜次数不小于数组的长度,那么最终答案将必然是数组中的最大元素,直接返回最大值即可。
而后,我们来考虑连胜次数小于数组长度的情况。
假设我们已经进行了m次比较,那么我们一定会有如下的结论:
那么,假设初始数组中位置 i d x idx idx上的数字为我们最终的答案,则一定有:
arr[0]
一定是arr[:k+1]
中的最大数字;arr[idx]
一定大于它前方的所有元素中的最大值(idx-1
次操作后的arr[0]
);[0: k+1]
中,一定不会有比它更大的数;这里,比较麻烦的问题在于对于 i d x > 0 idx > 0 idx>0的情况,在经过多次操作之后,当前的窗口是有可能包含之前的操作中补充到尾部的数据的,而这些补充的数据顺序也会和初始的顺序不同,因此无法直接给出。
但是,幸运的是,由我们前述的推论,m次操作之后补充在序列最后的数字一定小于当前的第一个元素,因此也必然小于当前的arr[idx]
,因此,我们事实上只要处理如下情况即可:
[idx: idx+k]
中的数据即可;[idx: n]
中的数据即可。如此,上述题目即可完成。
给出上述解题思路下的代码实现如下:
class Solution:
def getWinner(self, arr: List[int], k: int) -> int:
n = len(arr)
def get_window(i, k):
return arr[i: min(i+k, n)]
if k >= n:
return max(arr)
if arr[0] == max(get_window(0, k+1)):
return arr[0]
max_num = arr[0]
for i in range(1, n):
if arr[i] < max_num:
continue
max_num = arr[i]
if arr[i] == max(get_window(i, k)):
return arr[i]
return -1
提交代码后评测得到:耗时648ms,占用内存27.6MB。均属于当前第一梯队。
比较坑爹的是,看了一下其他人的解答,发现居然他们是暴力求解的,直接不停地进行比较操作直到连胜次数达到k。。。
我尝试了一下使用数组的pop和append操作,果然发现超时,但是当前的最优解却居然同样是相同的暴力解答思路,也是没啥想说的了。
他们暴力求解依然可以搞定的唯一原因在于他们用了python的collection库中的队列结构,它在出入队列的操作中执行效率更优。。。表示累感不爱。。。
给出他们的解答如下,有兴趣的读者可以参考一下,但反正我觉得是邪道。。。
from collections import *
class Solution:
def getWinner(self, arr: List[int], k: int) -> int:
if k >= len(arr) - 1:
return max(arr)
queue = deque(arr)
cnt = 0
while True:
a = queue.popleft()
b = queue.popleft()
if a > b:
cnt += 1
queue.append(b)
queue.appendleft(a)
else:
cnt = 1
queue.append(a)
queue.appendleft(b)
if cnt >= k:
return queue[0]
给出题目三的试题链接如下:
这一题就基本上等同于冒泡排序了,我们只需要考虑每一行恢复到下三角矩阵时所需要进行的交换操作的次数。
考察第 i i i行( i = 0 , 1 , 2 , . . , n − 1 i=0,1,2,..,n-1 i=0,1,2,..,n−1),当矩阵为下三角矩阵时,则其右方必有只有 n − 1 − i n-1-i n−1−i个0元素,因此,我们只要找到第一个符合这一条件的行(假设为 k k k)并将其进行移动即可。交换的次数即为k-i
。
需要注意的是,由于是下三角矩阵,因此,当对某一行 i i i进行操作时,我们无需考虑找到的行中的0元素多于目标行所需的0元素个数的情况,因为在此之前的行一定已经被填充完毕了。
下面,我们依照上述解题思路给出代码实现如下:
class Solution:
def minSwaps(self, grid: List[List[int]]) -> int:
n = len(grid)
def get_zero_num(arr):
return n - len("".join([str(i) for i in arr]).rstrip('0'))
counter = [get_zero_num(arr) for arr in grid]
ans = 0
for i in range(n-1):
idx = -1
for j, count in enumerate(counter):
if count >= n-1-i:
idx = j
break
if idx == -1:
return -1
ans += idx
counter.pop(idx)
return ans
提交代码测试得到:耗时572ms,占用内存14.7MB。为当前python代码提交结果中的最优算法。
给出题目四的试题链接如下:
这一题就是一道中规中矩的动态规划题目,坦率地说作为一道hard的题目,多少是有点名不副实的。
下面,我们快速地给出动态规划的思路如下:
nums1
)中的某一元素nums1[i]
:
nums1[i] + dp_nums1[i+1]
nums2[j] == num1[i]
),则从该元素开始的最大路径长度为:max(nums1[i] + dp_nums1[i+1], nums2[j] + dp_nums2[j+1])
最后,我们只需要比较dp_nums1[0]
与dp_nums2[0]
并返回较大值即可。
我们给出上述动态规划的实现代码如下:
class Solution:
def maxSum(self, nums1: List[int], nums2: List[int]) -> int:
MOD = 1000000007
l1 = len(nums1)
l2 = len(nums2)
dp = [[0 for i in range(l1+1)], [0 for i in range(l2+1)]]
i = l1-1
j = l2-1
while i >= 0 and j >= 0:
if nums1[i] > nums2[j]:
dp[0][i] = dp[0][i+1] + nums1[i]
i -= 1
elif nums1[i] < nums2[j]:
dp[1][j] = dp[1][j+1] + nums2[j]
j -= 1
else:
dp[0][i] = max(nums1[i] + dp[1][j+1], nums1[i] + dp[0][i+1])
dp[1][j] = max(nums1[i] + dp[0][i+1], nums2[j] + dp[1][j+1])
i -= 1
j -= 1
while i >= 0:
dp[0][i] = dp[0][i+1] + nums1[i]
i -= 1
while j >= 0:
dp[1][j] = dp[1][j+1] + nums2[j]
j -= 1
return max(dp[0][0], dp[1][0]) % MOD
提交代码后评测得到:代码耗时720ms,占用内存29.4ms。为当前python代码提交结果中的最优算法。