Python解题 - CSDN周赛第9期

这次比赛的题目难度又降低不少,基本上都是出现过的经典老题,最后一题更是曾多次出现在CSDN的每日一练中,看来官方是想提醒大家多多参与每日一练啊。


第一题:小艺读书

书是人类进步的阶梯。 小艺每周因为工作的原因会选择性的每天多读几页或者少读几页。

小艺想知道一本n页的书她会在周几读完。

分析

一开始以为是老题——给定n页的书,每天读3页或4页,然后想在某一天读完,有多少种读法。仔细一读,发现变得更简单了:题目给出了一个包含7个元素的列表,表示每天读多少页。然后题目就可以通过不断用总页数 n 减去每天读书的页数,直到 n 等于或小于0为止,即可得到需要读多少天。

唯一需要注意的是当读书的天数超过一周时,结果大于等于7,需要重新变成0开始。这也很简单,只要对7取余即可。该题简单到连结果都不需要转化为星期几,直接返回0到6的数字即可。

参考代码

def solution(n, pages):
    result = 0
    while n>0:
        n -= pages[result%7]
        result += 1
    return result%7

第二题:鬼画符门之宗门大比拼

给定整数序列A。求在整数序列A中连续权值最大的子序列的权值。

分析

题目标题起得很唬人,其实也是经典老题:求整数序列中连续子序列之和的最大值,LC原题。前几天在CSDN问答频道也有人问过,问哥还回答过,可惜没有被采纳。

解法有很多种,暴力解法也可,就是把所有的子序列求出来,然后比较它们的和,返回最大值。不过这种解法有可能因为耗时太长而无法通过测试,问哥也没有尝试。

时间复杂度为O(n)的解法是使用贪心算法或动态规划。算法的核心思想是:对序列中每一个整数而言,如果之前的序列之和是负数,就不相加。因为如果加上之前的序列,反而比自己本身的值还要小,而我们要找最大值,显然不符合,所以最大子序列应该从自己开始算起。

所以,额外定义两个变量,一个用来记录连续相加的子序列之和,一个用来记录最大的子序列之和。使用 max() 函数即可完成比较。当for循环遍历完一遍列表,即可返回结果。

唯一要注意的一点是,两个变量在初始化的时候,不需要表示“极小值”,而是直接赋值列表的第一个元素即可。这样可以假定列表只有一个元素,那么最大子序列当然就是第一个元素。然后遍历的时候从第二个元素开始比较。

参考代码

def solution(n, arr):
    result = temp = arr[0]
    for i in arr[1:]:
        temp = max(i, i+temp)
        result = max(result, temp)
    return result

第三题:硬币划分

有1分,2分,5分,10分四种硬币,每种硬币数量无限,给定n分钱(n<=100000),有多少中组合可以组成n分钱?

分析

四道题里稍微有点难度的,但也是老题。首先常识告诉我们,用暴力的穷举法一定能做出来。无非是四层嵌套循环abcd,分别表示1分、2分、5分、10分四种硬币,然后当下面等式成立的时候,结果加一:a+2*b+5*c+10*d=n

时间复杂度为O(n^{4}) 。因为这种方法太直观了,问哥一上来就试过,果不其然,无法通过。

该使用万能的动态规划了(此题其实属于动态规划中的完全背包类型)。

这里需要在逻辑上稍微理一理。我们要使用1、2、5、10分四种硬币得到n,那这个可能的方法数量可以由什么(状态)推导而来呢?我们用f(n)表示使用四种硬币组成价值n的方法数,在可能的结果里先拿走一枚10分的硬币(假设n>=10),那么剩下的硬币总价值为n-10。相对应地,f(n-10)表示使用这四种硬币有多少种方法可以组合成 n-10。假设规定最后这10分的价值只允许使用10分的硬币,那很显然,f(n)=f(n-10)。但是这10分、乃至价值n都同样可以使用1、2、5硬币来得到,所以我们要加上如果只使用1、2、5分硬币组成n的方法数,用f'(n)表示的话可以得到以下公式:f(n)=f(n-10)+f'(n)

不难发现,如果我们在f'(n)里拿走5分硬币,用f''(n)表示只使用1、2两种硬币得到n的方法数,同样可以推导出

f'(n)=f'(n-5)+f''(n)

为了使表达更清楚,我们更换一下f的表示方法,可得以下公式:

f^{2}(n)=f^{2}(n-2)+f^{1}(n)

f^{5}(n)=f^{10}(n-5)+f^{2}(n)

f^{10}(n)=f^{10}(n-10)+f^{5}(n)

其中f^{2}(n)f^{5}(n)f^{10}(n)分别代表:只使用1分2分硬币、只使用1分2分5分硬币、使用全部4种硬币得到价值 n 的方法数。而f^{1}(n),也就是只使用1分硬币的情况,方法数必然恒定是1,所以无须推导。

你可能会有一种“错觉”:这样会不会漏掉一些可能,比如那种“在前面使用了四种硬币,而在最后10分的时候只使用了1、2、5分硬币”?其实不然,这种情况必然已经包含在了公式的计算结果里,因为硬币是没有使用顺序的,你可以假设拿走的那一枚10分的硬币,是存在于“前面使用了四种硬币”里的某一枚(总的10分硬币的数量相同),这样,该情况就必然包含在了结果里。

根据上述推导,我们可以列出动态规划图表如下:

价值(n)012345678910111213
只使用1分硬币(f^{1}(n)11111111111111
只使用1分、2分硬币(f^{2}(n)11223344556677
只使用1分、2分、5分硬币(f^{5}(n)112234567810111314
使用全部四种硬币(f^{10}(n)112234567811121516

最终我们要返回的结果就是图表的右下角数字,如果列出二维数组的话,也就是dp[3][n]。 

参考代码

def solution(n):
    result = [[1]*(n+1) for i in range(4)]
    for i in range(2,n+1):
        k = 1
        for j in [2,5,10]:
            if i>=j:
                result[k][i] = result[k][i-j]+result[k-1][i]
            else:
                result[k][i] = result[k-1][i]
            k += 1
    return result[3][n]%1000000007 

该题因为要考虑到传统编程语言可能会存在得出的结果太大,产生数据溢出的问题,从而要求得到的结果对1000000007取模。所以,在返回的时候要将得到的结果进行模运算。


第四题:饿龙咆哮-逃离城堡

小艺酱误入龙族结界,被恶龙带回城堡,小艺酱决定逃离城堡,逃离龙族结界.。

总路程为c, 小艺酱的速度是vp,饿龙速度为vd。饿龙会在t小时后发现小艺酱出逃。

小艺酱担心自己跑不出去,准备了好多珍宝。 每当饿龙追上自己的时候小艺酱就会丢下一个珍宝,饿龙捡到珍宝会返回自 己的城堡进行研究,研究f小时后,再出城堡追赶小艺。

小艺想知道自己至少需要丢多少珍宝才能让自己安全逃出结界。

分析

CSDN每日一练里的题目,也是数学里再熟悉不过的追赶题——速度快的追速度慢的,问两人(物)相遇的时候,速度快的跑了多少路。因为每次饿龙捡到珍宝后,都要退回城堡再出发,于是本题就可以转化为,小艺要丢几次珍宝,才使得最后一次饿龙追不上小艺。而每次追赶的时候,小艺都先跑了 t 时间的路,所以 vp*t 就是每次追赶时的初始距离,设 x 为饿龙追上小艺所花的时间,易得 x = vp*t/(vd-vp),而vd*x 则等于饿龙追上小艺时跑的路程,于是可得,当最后一次追赶 大于结界总路程 c 的时候,代表饿龙追不上,而小艺成功逃出结界。

参考代码

def solution(vp, vd, t, f, c):
    result = 0
    if vd<=vp: return 0
    x = vp*t/(vd-vp)
    while vd*x<c:
        result += 1
        t += 2*x+f
        x = vp*t/(vd-vp)
    return result

虽然看起来似乎是官方在号召大家多多参加CSDN的每日一练,但问哥不得不吐槽,每日一练中的题目真的是文字描述问题太多,常常让人看不懂,而且测试数据也存在问题。问哥已经亲身遇到过其他平台的真题在CSDN无法通过测试。只希望官方能够重视起来,尽快对在线答题系统进行优化吧。

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

请叫我问哥

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

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

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

打赏作者

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

抵扣说明:

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

余额充值