Python HackerRank 刷题 Maximum Subarray Sum

题目地址: https://www.hackerrank.com/challenges/maximum-subarray-sum/problem?isFullScreen=false

简述:在给定正整数组中,找到任意连续子数组求和后对M取余的最大值。

栗子

当a = [3,2,7,4] 且 m=7 时,返回6。因为子数组[2,7,4]求和后对7取余可取到最大值6。

难度:Hard

解题

先正常搞。直接切片一行搞定。结果肯定是无法通过全部测试的。原因主要是因为切片的执行时间太长,其次是因为有2个for迭代。

def maximumSum(a, m):
    return max([sum(a[i:j]) % m for i in range(len(a)) for j in range(i+1,len(a)+1)])

通过分析子数组情况我们可知,例子中子数组 [3,2,7] 和子数组 [3,2] 存在交集或者说重复的部分。若我们已经知道子数组 [3,2] 对m求余的值为R[3,2],则 [3,2,7] 对m取余的值 R[3,2,7] = (R[3,2] + 7) % m。知道这一点以后,我们便可以构建数组a的prefix sum 来解决这个问题。

接上面的例子,数组a的对m求余的prefix sum数组presum_a = [3,5,5,2],其中

presum_a[1] = (presum_a[0] + a[1]) % m = (3+2)%7 = 5

presum_a[2] = (presum_a[1] + a[2]) % m = (5+7)%7 = 5

当我们有了presum_a以后,事情就好办了。由于求余运算的性质,presum_a中每个元素取值范围为 [0, 7) ,并且通过观察我们可知presum_a[3] < presum_a[0],可知a的子数组[2,7,4]是一个值得考虑的对象。因为当j > i时,presum_a中任意两个元素差值的绝对值abs(presum_a[i] - presum_a[j])就代表a中从i到j的子数组求和并对m取余的值,与m之间的差值,即

abs(m - (a[i] +... + a[j])%m)

若这个差值越小,则代表我们我们求和取余的值越大。因此,由于abs(presum_a[3] - presum_a[0]) = 1,这代表a[1]到a[3]至少加了一个6或者6的倍数,而6是对7求余能够得到的最大值。

那么问题就简单啦,根据上面描述的例子,当我们只需要找到presum_a中任意两元素差值绝对值的最小值d = min(abs(presum_a[i] - presum_a[j])),并且拿m - d与presum_a中的最大值作比较max(max(presum_a), m-d),选择两者中大的那一个即可。

def maximumSum(a, m):
    # 原始数组a的值在做完prefix sum后没有价值了,不需要保存,因此可直接在原数组上滚。
    a[0] = a[0]%m
    for i in range(1,len(a)):
        a[i] = (a[i - 1] + a[i])%m

    max_d = max(a)                              # 拿到滚出来的最大值

    a = list(zip(a,[i for i in range(len(a))])) # 把元素index贴上,用来判断j > i
    a.sort()                                    # 来一手排序,减少找任意两元素差值最小值的计算量
    d = min([abs(a[i][0]-a[i+1][0]) for i in range(len(a)-1) if a[i][1] > a[i+1][1]])
    return max(max_d,m-d)

程序整体除了排序O(nlogn)时间,其余O(n)时间,因此程序整体O(nlogn)时间。

### 回答1: 以下是一个经典的 Maximum Sum 子数组问的动态规划代码: ```python def max_sum_subarray(nums): if not nums: return 0 n = len(nums) dp = [0]*n dp[0] = nums[0] max_sum = nums[0] for i in range(1, n): dp[i] = max(dp[i-1]+nums[i], nums[i]) max_sum = max(max_sum, dp[i]) return max_sum ``` 该算法的时间复杂度为 $O(n)$,其中 $n$ 是数组的长度。 ### 回答2: Maximum sum是一个经典的动态规划问,其目标是在一个给定的数组中找到一个具有最大和的子数组。 在解决这个问时,可以定义一个一维动态规划数组dp,其中dp[i]表示以第i个元素结尾的子数组的最大和。那么,可以得出动态规划的转移方程如下: dp[i] = max(dp[i-1] + nums[i], nums[i]) 其中,nums表示给定的整数数组。 接下来,可以使用一个变量maxSum来记录所有子数组的最大和。遍历整个数组,更新dp[i]的同时,不断更新maxSum的值,即可得到最终的结果。 下面是该问的动态规划代码实现: ```python def maxSum(nums): dp = [0] * len(nums) maxSum = float('-inf') dp[0] = nums[0] maxSum = max(maxSum, dp[0]) for i in range(1, len(nums)): dp[i] = max(dp[i-1] + nums[i], nums[i]) maxSum = max(maxSum, dp[i]) return maxSum ``` 该算法的时间复杂度为O(n),其中n为数组的长度。使用动态规划的思想,可以高效地解决Maximum sum。 ### 回答3: 动态规划(Dynamic Programming)是一种常用的算法思想,可以解决一些最优化问Maximum Sum是一种经典的动态规划问,目标是找出一个数组中最大的子数组和。 要编写Maximum Sum的动态规划代码,可以按照以下步骤进行: 1. 首先定义一个变量max_sum,用于记录当前最大的子数组和,初始化为数组中的第一个元素(即max_sum = arr[0])。 2. 然后定义一个变量cur_sum,用于记录当前的子数组和,初始化为数组中的第一个元素(即cur_sum = arr[0])。 3. 接着,使用一个循环遍历数组中的每一个元素(从第二个元素开始): (1)如果当前子数组和cur_sum加上当前元素arr[i]大于当前元素arr[i]本身,说明加上当前元素后,子数组和变得更大,因此更新cur_sum为cur_sum + arr[i]。 (2)否则,当前元素arr[i]比当前子数组和cur_sum更大,说明当前元素作为新的起点,重新开始构建子数组,即令cur_sum = arr[i]。 (3)将当前子数组和cur_sum与当前最大的子数组和max_sum进行比较,如果cur_sum大于max_sum,则更新max_sum为cur_sum。 4. 最后,返回最大的子数组和max_sum作为最终结果。 下面给出这个算法的代码实现: ```python def maximum_sum(arr): max_sum = arr[0] cur_sum = arr[0] for i in range(1, len(arr)): if cur_sum + arr[i] > arr[i]: cur_sum += arr[i] else: cur_sum = arr[i] if cur_sum > max_sum: max_sum = cur_sum return max_sum ``` 这段代码的时间复杂度为O(n),其中n为数组的长度,因为需要遍历整个数组。在使用动态规划思想解决Maximum Sum时,可以通过定义合适的状态和状态转移方程来简化问,并提高算法的效率。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值