中文的leetcode果然和我犯冲,这次比赛又是桑心的一逼,题目倒还好,虽然第二第三题卡了一会,但是好歹都做出来了,但是最坑的是第四题居然比赛的时候没做出来,然后比赛结束之后10分钟给搞定了。。。10分钟,就差这10分钟。。。
唉,见鬼了。
整体排名不算差吧,国内前200,世界500多点,但是考虑到双周赛参加的人本来就少,这个成绩就很尴尬了,更何况我就差那么10分钟。。。
算了,不口吐芬芳了,毕竟一晚上过去,也多少看淡了一点,好好梳理一下,然后再接再厉吧。。。
≧ ﹏ ≦
给出题目一的试题链接如下:
第一题的题目算是中规中矩吧,目标是计算奇数长度的连续子序列的总和的总和。
因此,我们只需要进行一个二重循环遍历就行了。
不过,为了更好地提升代码效率,我们可以求出累积数组,而后序列的求和操作就可以直接换算为一个减法操作就可以了,可以省掉一层循环。
给出代码实现如下:
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,但是看了一下算法思路本身乃至实现都是几乎完全一样的,因此就不再过多赘述了。
给出题目二的试题链接如下:
这一题的算法思路本身还是蛮清晰的,其实就是看requests中所有请求之间的重合部分,并按照重合次数对idx进行排序,然后分别从大到小向其中填充入原数组中的元素即可。
但是,比较难的一点在于如何去实现这个算法,要计算重合次数本身并不是一件很轻松的事。
后来倒是想到了一种个人认为比较巧妙的方法,就是对于每一个requests,在首部+1,尾部-1,而后求整个数组的累积和,这样每个idx中的累积和就是这个idx在requests中被重复请求的次数。
从而,我们只需要对idx进行排序,再对数组进行排序,然后对应元素相乘求和即可得到我们最终的答案。
给出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函数。
因此,这里也就不在多做赘述了。
给出题目三的试题链接如下:
第三题我在比赛的时候倒是超时了两次,不过结果发现一次是因为读题失误,一次则是因为数据处理上的问题,思路上倒是一直没啥大的问题,所以后来就直接改对了,算是可喜可贺吧,呵呵。
这一题的解题思路其实也是蛮清晰的,要使得整个数组之和最终能够被p整除,那么我们要做的首先就是求出总的数组之和对p得余数,假设为r,如果r为0,说明本身就能够被整除,那么直接返回0即可;反之,就是去找其中每一个元素k,假设sum[:k+1] % p = r'
,那么需要删除的最短序列长度就是下一个使得sum[:k+l] % p = r' + r
的数组长度l。
剩下的问题就是代码实现了。
我们给出上述思路的一种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,差不多有一倍的差距,说明上述实现肯定有什么改进的地方。
看了一下当前最优的解法,发现思路是一致的,但是他们的统计是计算最近一个和为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。为当前最有方案。
给出题目四的试题链接如下:
这一题真的可惜了,比赛的时候没能解出来,但实际上这一题并没有很难,思路对了的话花不了太多的时间。
这一题真的可惜了,比赛的时候没能解出来,但实际上这一题并没有很难,思路对了的话花不了太多的时间。
事实上,这一题的思路就是反向画回去,从最顶层的图层开始绘制,一直绘制到不能绘制为止。
首先,我们找到每一种颜色的原始绘制区域,然后,看他是否属于当前的最顶层图层,如果是,那么将其上色,反之就先等等。如果能够一直绘制到底,将所有的颜色都涂上,那么就说明可以实现,反之就说明不能绘制。
给出代码实现如下:
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,差了差不多一半的时间,如果有兴趣的读者可以自行去看一下,这里我就暂时不做什么研究了。。。