Leetcode276周周赛
写在前面
第一次参加周赛,写了十分钟,就把第一题写出来,就去忙其它事去了,但是后来下来看了一下题,也没有想象中那么困难,就写一篇题解记录一下吧。
第一题
e
a
s
y
easy
easy题,就是浪。先求整数倍
n
n
n,分割出
n
n
n个等长字符串,然后求余,没有当然最好,有
y
y
y个就在后面补充
k
−
y
k-y
k−y个
f
i
l
l
fill
fill字符串就行了
class Solution:
def divideString(self, s: str, k: int, fill: str) -> List[str]:
n = len(s)
n1, n2 = n // k, n % k
lis = []
for i in range(n1):
lis.append(s[k*i:k*i+k])
if n2 != 0:
s = s[-n2:] + fill * (k-n2)
lis.append(s)
return lis
第二题
这道题需要从后往前做,因为这样可以最大限度的保证对倍数 m a x D o u b l e s maxDoubles maxDoubles的利用,如果从前往后进行动态模拟的话,很难选定翻倍的时机,但是从后往前就没有这种顾虑,只要是二的倍数,除就完了,利用完 m a x D o u b l e s maxDoubles maxDoubles的次数,剩下的减到1的次数就是 t a r g e t − 1 target-1 target−1(这里target是已经递减之后的)次。
class Solution:
def minMoves(self, target: int, maxDoubles: int) -> int:
if target == 2:
return 1
if maxDoubles == 0:
target -= 1
return target
times, time = 0, 0
while 1 < target:
if times < maxDoubles:
if target % 2 == 0:
target //= 2
times += 1
else:
target -= 1
time += 1
else:
time += target - 1
break
return time
第三题
简单的动态规划题目,算不上难,但是这个题目确实嘲讽到我了,因为我一开始没有写出来。
最开始按照我的想法,我是先建立一个大小为
n
=
l
e
n
(
q
u
e
s
t
i
o
n
s
)
n=len(questions)
n=len(questions)的数组
d
p
dp
dp,每一个位置保存从前往后的
q
u
e
s
t
i
o
n
s
[
i
]
[
0
]
questions[i][0]
questions[i][0]的累加的数,然后遍历一次,把每一个不能累加到某个位置的
q
u
e
s
t
i
o
n
s
[
i
]
[
0
]
questions[i][0]
questions[i][0]减去就行了,写出来代码如下:
class Solution:
def mostPoints(self, questions: List[List[int]]) -> int:
n = len(questions)
dp = [0] * n
for i in range(n):
dp[i] = questions[i][0] + dp[i-1]
for j in range(n):
k = questions[j][1]
for i in range(1,k+1):
if i + j < n: #确定遍历范围在数组大小内
dp[i+j] -= questions[j][0]
return max(dp)
咋一看没有什么错误,但是实际上是有的。比如说测试用例[[1,1],[2,2],[3,3],[4,4],[5,5]],最大的是
2
+
5
=
7
2+5=7
2+5=7,但是递减到
5
5
5的时候由于
1
1
1的范围覆盖到
2
2
2,与
2
2
2无法兼容,筛选条件却没办法满足这一条,导致我的最大值比应该的值大。
为了解决这个问题,我思考是不是要添加判定条件,但是却始终想不出好的限制条件。于是转化思路。从后往前遍历。
其实思路大同小异,前面是先累加,最后再减去不符合条件的,但是由于覆盖范围的原因筛选不完。所以不累加,直接从后往前,满足条件的加上,不满足的就不加。限制条件就还是覆盖范围而已。
class Solution:
def mostPoints(self, questions: List[List[int]]) -> int:
n = len(questions)
dp = [0] * n
for i in range(n - 1, -1, -1):
j = i + questions[i][1] + 1
if i == n-1:
if j < n:
dp[i] = questions[i][0] + dp[j]
else:
dp[i] = questions[i][0]
else:
dp[i] = max(dp[i + 1], questions[i][0] + (dp[j] if j < n else 0)) #这样写要简单一点
return dp[0]
第四题
我们先来做一个简单的分析,最理想的状态就是同时使用
t
i
m
e
=
⌊
s
u
m
(
b
a
t
t
e
r
i
e
s
)
n
⌋
time=\lfloor\frac{sum(batteries)}{n}\rfloor
time=⌊nsum(batteries)⌋分钟。但天不遂人愿,有的时候它就是不让你这么简单,最简单的反例就是数组中有时间大于
t
i
m
e
time
time的时候,我们只要排除掉数组中大于
t
i
m
e
time
time(注意,这里的
t
i
m
e
time
time是每一次计算之后的time,不是一开始固定不变的
t
i
m
e
time
time)的个数,然后再次计算
t
i
m
e
time
time,一直到数组中的时间小于该数组下的
t
i
m
e
time
time,这就是我们所需要的
t
i
m
e
time
time了。
最开始我写的代码如下:
class Solution:
def maxRunTime(self, n: int, batteries: List[int]) -> int:
def judge(batteries,n):
time = sum(batteries) // n
for i in range(len(batteries)):
if batteries[i] > time:
batteries.remove(batteries[i])
n -= 1
time = sum(batteries) // n
for j in range(len(batteries)):
if batteries[j] > time:
return judge(batteries,n)
return time
return judge(batteries,n)
他能通过大部分测试用例,但是当我创建测试用例 5 , [ 5 , 1 , 9 , 5 , 5 ] 5,[5,1,9,5,5] 5,[5,1,9,5,5](我自己创建的用例)会出现数组越界,所以就很尴尬。然后换了个写法
class Solution:
def maxRunTime(self, n: int, batteries: List[int]) -> int:
batteries.sort()
batteries.reverse() #将数组从大到小排列,保证遍历到的是最大值
'''
实不相瞒,在写这篇文章的时候,我觉得把上面那串代码数组这样排列一下,也就能对,
但是看过前面我的文章的人都知道我是什么德行,着实有点难,大家可以自己去试试,
要是能对别忘了回来告诉我。
'''
start_time = sum(batteries)
for i in range(len(batteries)):
if batteries[i] <= start_time // n:
return start_time // n
start_time -= batteries[i]
n -= 1
写在后面
讲几件事情吧,先是刷题的一些心得,我是这个寒假开始才有意识、有规律的开始刷题的,刷了几次题,写了两套题解,也有了一些想法。
其中感受最深的是一开始刷题的时候不注重数学规律,拿到题目的第一想法就是 模 拟 实 现 模拟实现 模拟实现,比如像第四题,第一想法就是建立电脑列表和电池列表进行一一匹配,然后就是 动 态 规 划 动态规划 动态规划的题目,这种心态真的特别强烈,但是这种想法是要不得的,算法更加注重的数学逻辑,这是我们应该特别注意的。
这是我特别深的体会,写出来与大家分享,希望与诸君共勉。
第二就是我文章的问题,由于我是一个菜鸟,所以我深能体会大家(假装很多人和我一样菜)看网络上一些大佬的题解的那种无奈,他就告诉你,动规、贪心、回溯…你说他什么都没写吧,人家大佬愿意和你分享代码就很不错了,但是你说他写了吧,嗯…
所以我写题解一开始就是两个定位,一是反馈自己的学习情况,二是写一些菜鸟也能看懂的题解,所以希望大家监督,时刻提醒我的文章写得怎么样,是否有错,又或者当你遇见
看
不
懂
的
知
识
点
看不懂的知识点
看不懂的知识点我却
没
有
详
细
没有详细
没有详细书写的时候,请尽快向我反应。我会尽快改正的。