力扣285场周赛做题记录:射箭比赛中的最大得分 01背包,回溯,路径还原 python

博主分享了自己备战蓝桥杯比赛的经历,决定全心投入并为此调整博客更新计划。在周赛中体验到力扣题目的严谨性,并参与了射箭比赛算法题的解答,尝试了01背包解决最大得分问题。虽然遇到超时问题,但通过学习他人解决方案,理解了路径还原的方法。博主表示将努力提高解题速度并期待比赛后的回归。
摘要由CSDN通过智能技术生成

想说的话

大家好🌼🌼,我是 @愿此后再无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/
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愿此后再无WA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值