📌  相关文章
📜  国际空间研究组织 | ISRO CS 2020 |问题 43(1)

📅  最后修改于: 2023-12-03 15:37:16.215000             🧑  作者: Mango

国际空间研究组织 | ISRO CS 2020 | 问题 43

题目描述

有一个长度为N的整数数组a,要求把这个数组分为至多三个子数组,使得每个子数组的和最大值最小。并返回这个最小值。例如,输入数组a = [1,2,3,4,5,6,7,8,9],则输出为最小值为17,分为[1,2,3,4,5][6,7][8,9]三个子数组。

输入
  • 一个长度为N的整数数组a,其中1<=N<=10^5,1<=a[i]<=10^9
输出
  • 所有分组中,子数组的最大和的最小值。
示例输入

[1,2,3,4,5,6,7,8,9]

示例输出

17

解题思路

这是一道二分答案+贪心的题目。

首先需要二分答案,设答案为limit,则问题转化为:能够把数组a分为不超过k个子数组,使得每个子数组的和不超过limit。

知道了问题,我们来看这个问题如何求解:

遍历一遍数组a,设sum为当前子数组的和,cnt为当前子数组的数量,当sum + a[i] > limit时,表示当前子数组要换下一组了;当cnt = k时,表示子数组的数量已经达到限制,也需要换下一组子数组。

具体来说就是,不断累加数组a的每个元素,如果当前的和大于limit,那么就分为下一个子数组;如果当前子数组数量达到了k个,那么就结束这个方案,尝试判断下一个方案。

这样一直尝试每个方案,直到找到最优解。至于是否继续尝试,可以通过判断是否满足每个子数组数量都不超过k来判定。

代码示例
def can_split(a, n, k, limit):
    cnt = 1
    curr_sum = 0
    
    for i in range(n):
        if a[i] > limit:
            return False
        
        curr_sum += a[i]
        if curr_sum > limit:
            cnt += 1
            curr_sum = a[i]
        if cnt > k:
            return False
    
    return True

def maximize_min_sum(a, n, k):
    start = 1
    end = sum(a)
    result = 0
    
    while start <= end:
        mid = (end + start) // 2
        
        if can_split(a, n, k, mid):
            result = mid
            end = mid - 1
        else:
            start = mid + 1
    
    return result
 
a = [1,2,3,4,5,6,7,8,9]
n = len(a)
k = 3
result = maximize_min_sum(a, n, k)
print(result)

返回:

17

代码片段中,can_split函数表示,如果对于每个子数组的最大和的限制是limit,并且将数组a最多分成k个子数组,是否存在一种分组方法,使得每个子数组的和都不超过limit。

具体实现中,我们遍历一遍数组a,遇到一个a[i]的值大于limit,则返回False。否则,累加数组a的每个元素,如果当前的和大于limit,那么就分为下一个子数组;如果当前子数组数量达到了k个,那么就结束这个方案,尝试判断下一个方案。

maximize_min_sum函数是二分答案的主函数,具体流程是,一开始让start=1,end=sum(a),并设置初始结果为0。当start <= end时,我们计算mid=(start+end)//2,如果当前的限制使得我们分的子数组数量超过了k,那么就把限制再加大一些,尝试找到一个可行解,即可以划分为不超过k个子数组,每个子数组和的上限不超过mid。

如果当前限制下可以划分为不超过k个子数组,那么就记录下当前的mid,然后尝试让限制再小一些,找寻下一个更优的mid。如果最终的mid是可行的,则会得到答案otal_sum。

运行结果为17,和示例给出的结果相同,说明代码实现的正确性有保证。