最优分割
牛客网:最优分割
二分法的简单运用
题目描述
依次给出n个正整数A1,A2,… ,An,将这n个数分割成m段,每一段内的所有数的和记为这一段的权重, m段权重的最大值记为本次分割的权重。问所有分割方案中分割权重的最小值是多少?
输入描述:
第一行依次给出正整数n,m,单空格切分;(n <= 10000, m <= 10000, m <= n)
第二行依次给出n个正整数单空格切分A1,A2,… ,An (Ai <= 10000)
输出描述:
分割权重的最小值
示例1
输入
5 3
1 4 2 3 5
输出
5
说明
分割成 1 4 | 2 3 | 5 的时候,3段的权重都是5,得到分割权重的最小值。
思路:
- 二分逼近法
- 这个题的意思:假设存在数组 1 4 2 3 5 分割成 3 段,有几种分法呢,答案是 C4^2: 43/21 = 6 种,
- 即在数组的四个间隔中插入两根柱子将其分成 3 段,每一种分法中会对应有 3 个子数组的值,其中最大的值即为当前分割方法的
- 最大权值,在所有的分割方法中找出最小的一个最大权值,听起来好像有点绕
- eg:1 | 4 2 | 3 5 这种分割方法,它的最大权值为 8 而: 1 4 | 2 3 | 5 分割方法,它的最大权值为 5
- 思路:假设存在一个最大值的最小值 x,反过来划分数组。子数组的权值都比x要小,如果组数小于m,说明 x 还可以再小;
- 组数大于m,说明 x 需要变大,以容纳更多的数。减小分组数。如果组数等于m,x也可能再小
- 考虑边界情况,现在把每个元素分成一组,那么x的最小值就是数组中最大的值;把数组当成一个组,那么x就是数组元素之和。
- 即 max(nums) <= x <= sum(nums)
- 因为每一组都是连续的,只要每一组累加的和大于了x,那么当前元素就要放到下一组,记录有多少组即可。
- 我们通过二分逼近来确定这个x的值。
- 在于这个“逼近”,这道题是在连续的数值范围中逼近,换句话说,每个组的和一定在范围之内,因此正确答案是不会被跳过的;
# -*- coding: utf-8 -*-
def bisearch(arr, m): #参考高赞回复的答案
left = max(arr)
right = sum(arr)
while left < right:
mid = (left + right)//2
sets = 1
cur = 0
for num in arr:
if cur + num > mid:
sets += 1
cur = 0
cur += num
if sets > m:
left = mid+1
else:
right = mid
return left
if __name__=='__main__':
n,m = list(map(int,raw_input().split()))
num = list(map(int,raw_input().split()))
print(bisearch(num,m))
- 动态规划法:
- dp[i][j] 代表前面i个数被分成m个区间的权值
空间复杂度略高
def dp_array(num,n,m):
dp = [[float('inf') for _ in range(m+1)] for _ in range(n+1)]
dp[0][0] = 0
for i in range(1,n+1):
for j in range(1,m+1):
for k in range(i):
dp[i][j] = min(dp[i][j],max(dp[k][j-1],sum(num[k:i])))
return dp[n][m]
if __name__=='__main__':
n,m = list(map(int,raw_input().split()))
num = list(map(int,raw_input().split()))
print(dp_array(num,n,m))