想说的话
大家好🌼🌼,我是 @愿此后再无WA,可以叫我小A,也可以叫我愿愿💡💡,一位阳光帅小伙,对算法领域比较感兴趣。如果我的文章对您有用,欢迎持续关注,我们一起进步!🎈🎈
很抱歉各位😪😪,现离蓝桥杯比赛不到一个月时间,我临时改变了计划,转为全心备战蓝桥,因为这个省一对我来说太重要了,也是我最后一次机会,我一定要拿到手📌📌,那么这样的话我在博客上花的时间就会少了很多,也将导致博客文章质量明显下降,在此我给大家说声抱歉💥💥
🌟🌟这些日子我真的很开心,博客上能遇到一群志同道合的兄弟姐妹真的很幸福,没有你们的支持与鼓励我早就坚持不下去了,因为有你们我才能走的更远☀️☀️熬过这段时期我一定会回来的,爱你们❤️❤️
吐槽
第一次参加力扣周赛,感觉还是很不错的,力扣出的题目真的很严谨,看起来非常舒服,不会出现看不懂题的现象,体验真的很棒。以前觉得自己不是接触这些的料,后来跟着刷题群的好兄弟们就一起去了。一共四道题,做出了两道,第三道套了01背包就没时间了。从中我也发现自己很多的不足,做题贼慢,明明很容易做的题却要花上二三十分钟,哎还得多多练习啊。
射箭比赛中的最大得分
分析
这就是周赛的第三道题,很容易理解,就是每个区域要比alice射的箭多一只即可,如果还有剩余的箭的话就随便给一个区域。
求最大得分的话可以用01背包来解,以numArrows作为背包容量,计分区域作为物品数量,然后aliceArrows作为物品重量,套用01背包模板即可得出最大得分。问题来了,以前做过的题目都是求最大价值的,而这里是求每个区域的箭数。
比赛结束后我又做了一下,想着把剩下没做完的补上,一开始我以为能够用dp凑数,发现太多种组合了不好实现。然后我就想了用子集,从大到小枚举每个数之和,因为数据很小就0~12,所以应该很容易凑出第一个符合条件的结果然后返回即可。试了一下超时了。
class Solution:
def maximumBobPoints(self, numArrows: int, aliceArrows: List[int]) -> List[int]:
dp = [0 for i in range(numArrows+1)]
for i in range(1,12):
for j in range(numArrows,aliceArrows[i]-1,-1): # 9 1
if j-aliceArrows[i]-1 >= 0:
dp[j] = max(dp[j],dp[j-aliceArrows[i]-1]+i)
r = dp[-1]
res = []
#print(r)
def dfs(s,cnt):
if cnt == r:
#print(res,cnt)
c = 0
for i in res:
c += aliceArrows[i] + 1
if c <= numArrows:
return True
for i in range(s,0,-1):
if cnt+i > r:
continue
res.append(i)
cnt += i
if dfs(i-1,cnt):
return True
cnt -= i
res.pop()
dfs(11,0)
barrows = [0 for i in range(12)]
cnt = 0
for i in res:
barrows[i] = aliceArrows[i] + 1
cnt += barrows[i]
barrows[0] = numArrows-cnt
return barrows
于是我就看了大佬们的思路,找了好久都是c++的没有python,看的很难受,但没办法我就顶着头皮看下去了,有状压DP,和01背包+路径还原,我果断选择了后者,研究了半天终于搞懂了。
路径还原就是找结果的逆过程,在花费相同箭数的情况下通过比较前i件物品与前i-1件物品的最大价值,如果前者大于后者,说明在决策第i件物品是否选择时是选择了第i件物品,而选择了这件物品就要减去这物品需要花费的箭数,返回到前一个状态,不断重复上面动作即可找出全部状态。如果最后还有箭数多的话就任给一个区域即可。
class Solution:
def maximumBobPoints(self, numArrows: int, aliceArrows: List[int]) -> List[int]:
dp = [[0 for i in range(numArrows+1)] for j in range(12)]
# 无需初始化,因为区域0也是一种情况,而其本身得分就是0
for i in range(1,12):
for j in range(1,numArrows+1):
dp[i][j] = dp[i-1][j]
if j-aliceArrows[i]-1 >= 0:
dp[i][j] = max(dp[i-1][j],dp[i-1][j-aliceArrows[i]-1]+i)
res = [0 for i in range(12)]
# 路径还原
for i in range(11,0,-1):
# 如果大于就说明选择了第i个区域,而如果等于说明从i-1到i转移的过程没有选第i个区域
if dp[i][numArrows] > dp[i-1][numArrows]:
# 既然选择了i区域,那么使用的箭数必定大于alice命中i区域的箭数,至少大于1,就取最小
res[i] = aliceArrows[i] + 1
numArrows -= res[i] # 相应的,既然选择了i区域,那么i-1
res[0] = numArrows # 如果有多的就随便给一个区域
return res
但提交后会发现还是超时了,我对比了一下大佬的C++代码,发现除语言不同外没有任何区别,可能python就是慢点吧,如果分语言赛道比赛的话应该不会出现这种情况。
最后看另一位大佬的AC代码,我还没理解,等会再研究下,应该是类似于01背包的递归实现。
AC代码
class Solution:
def maximumBobPoints(self, n: int, a: List[int]) -> List[int]:
l = len(a)
def dfs(i, c):
if i == l - 1:#如果只剩最后一个区域了,那么把所有箭都射出去
if c > a[i]:#得分
return i, [c]
else:#不得分
return 0, [c]
if c > a[i]:#如果可以得分
x1, y1 = dfs(i + 1, c - a[i] - 1)#拿分的选择
x1 += i
x2, y2 = dfs(i + 1, c)#不拿分的选择
if x1 > x2:#选择得分和不得分的最优解
return x1, [a[i] + 1] + y1
else:
return x2, [0] + y2
else:#剩余箭的数量已经不能得分,直接去下一个区域
x, y = dfs(i + 1, c)
return x, [0] + y
return dfs(0, n)[1]
class Solution:
def maximumBobPoints(self, n: int, a: List[int]) -> List[int]:
l = len(a)
def dfs(i, c):
if i == l - 1:#如果只剩最后一个区域了,那么把所有箭都射出去
if c > a[i]:#得分
return i, [c]
else:#不得分
return 0, [c]
if c > a[i]:#如果可以得分
x1, y1 = dfs(i + 1, c - a[i] - 1)#拿分的选择
x1 += i
x2, y2 = dfs(i + 1, c)#不拿分的选择
if x1 > x2:#选择得分和不得分的最优解
return x1, [a[i] + 1] + y1
else:
return x2, [0] + y2
else:#剩余箭的数量已经不能得分,直接去下一个区域
x, y = dfs(i + 1, c)
return x, [0] + y
return dfs(0, n)[1]
# 作者:qin-qi-shu-hua-2
# 链接:https://leetcode-cn.com/problems/maximum-points-in-an-archery-competition/solution/python-hui-su-by-qin-qi-shu-hua-2-mv1w/