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

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

国际空间研究组织 | ISRO CS 2007 |问题 59

题目描述

给定一个长度为N(N<=1000)的数组,元素均为正整数,将该数组分成M(M<=N)个非空的连续子数组,每个子数组的元素之和记为该子数组的权值。 定义所有子数组的权值中的最大值为该分割的权值。 找到一种最优的分割方案,使得分割后所有子数组的权值中的最大值最小。

输入格式

第一行输入n和m,表示数组长度和分割数 第二行输入n个正整数,表示给定数组

输出格式

输出最小的最大子数组和

示例

输入:

5 3
1 2 3 4 5

输出:

5
解题思路

这道题是对"贪心"算法的一个经典应用,可以采用二分答案进行求解。

总体思路为将数组的元素二分,每次进行判断,将数组按照划分的策略,划分为连续的子数组,并根据所划分的策略计算所有子数组的权值。如果权值最大的子数组小于等于二分的值,则更新全局最小值,否则就抛弃当前状态,继续二分。

代码实现
def check(mid):
    cnt = 1
    cur_sum = 0
    for i in range(n):
        if(a[i] > mid):
            return False
        if(cur_sum + a[i] > mid):
            cnt += 1
            cur_sum = a[i]
        else:
            cur_sum += a[i]
    return cnt <= m

n, m = map(int, input().split())
a = list(map(int, input().split()))

l = 0
r = sum(a)
ans = r
while(l <= r):
    mid = (l + r) // 2
    if(check(mid)):
        ans = min(ans, mid)
        r = mid - 1
    else:
        l = mid + 1

print(ans)

代码分析:

首先得到题目给的输入:

n, m = map(int, input().split())
a = list(map(int, input().split()))

因为我们要用到二分法,需要设置左右边界:

l = 0
r = sum(a)
ans = r

继续进入二分的循环,每一次取中间值,并按照贪心算法的策略进行计算:

while(l <= r):
    mid = (l + r) // 2
    if(check(mid)):
        ans = min(ans, mid)
        r = mid - 1
    else:
        l = mid + 1

check函数用来判断方案是否可行,每次传入一个mid,计算分割后权值中的最大值,如果最大值小于等于mid,则代表方案可行,否则不行。

def check(mid):
    cnt = 1
    cur_sum = 0
    for i in range(n):
        if(a[i] > mid):
            return False
        if(cur_sum + a[i] > mid):
            cnt += 1
            cur_sum = a[i]
        else:
            cur_sum += a[i]
    return cnt <= m

最后输出结果即可:

print(ans)

至此,本题已经成功解决。