第一行依次给出正整数n,m,单空格切分;(n <= 10000, m <= 10000, m <= n)
第二行依次给出n个正整数单空格切分A1,A2,… ,An (Ai <= 10000)
分割权重的最小值
5 3 1 4 2 3 5
5
分割成 1 4 | 2 3 | 5 的时候,3段的权重都是5,得到分割权重的最小值。
// 结果在(max(nums),sum(nums))之间,使用二分查找
// 以[7,2,5,10,8]举例,开始假设只有一个子数组,set=1
// 第一个 mid = (10+32)/2=21, 然后把数字一个一个塞进去
// 先塞7, 7<21, 7+2<21, 7+2+5<21, 直到 7+2+5+10>21
// 意味着一个数组放不下, set+1=2, 然后把后面的塞完
// 如果比m大说明我们开的子数组太多, 也就意味值我们数组容量(mid)太小
// 所以我们就从[22,32]区间中找, 否则在[10,21]中找
import java.util.Scanner;
public class Main {
public static int splitArray(int[] nums, int m) {
int max = Integer.MIN_VALUE, sum = 0;
for (int i : nums){
sum += i;
max = Math.max(max,i);
}
int left = max, right = sum;
int mid, set, cur;
while(left < right){
mid = (left+right)/2;
// m 是子数组数,不是cut数
set = 1;
cur = 0;
for(int i : nums){
if(cur+i > mid){
set++;
cur = 0;
}
cur+= i;
}
if(set > m) left = mid + 1;
else right = mid;
}
return left;
}
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[] nums = new int[n];
for (int i=0; i<n; i++){
nums[i] = sc.nextInt();
}
System.out.print(splitArray(nums,m));
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Solution25_最优分割 {
public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
String[] line1 = bf.readLine().split(" ");
int n = Integer.parseInt(line1[0]);
int m = Integer.parseInt(line1[1]);
String[] line2 = bf.readLine().split(" ");
int[] nums = new int[n];
int sum = 0;
int max = 0;
for (int i = 0; i < n; i++) {
nums[i] = Integer.parseInt(line2[i]);
if (max < nums[i]) {
max = nums[i];
}
sum += nums[i];
}
System.out.println(binarySearch(nums, m, n,max,sum));
}
/**
* 二分逼近法
* 这个题的意思:假设存在数组 1 4 2 3 5 分割成 3 段,有几种分法呢,答案是 C4^2: 4*3/2*1 = 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的值。
* 在于这个“逼近”,这道题是在连续的数值范围中逼近,换句话说,每个组的和一定在范围之内,因此正确答案是不会被跳过的;
*/
private static int binarySearch(int[] nums, int m, int n, int left, int right) {
int ans = right;
while (left <= right) {
int mid = (left + right) / 2;
int sum = 0;
int count = 1;//记录数组的个数
for (int i = 0; i < n; i++) {
//直到当前子数组的和加上当前元素比mid还大,那必须将当前元素归为下一个子数组中,sum重新计算新子数组的和
if (sum + nums[i] > mid) {
count++;
sum = nums[i];
} else {//当前子数组的和比mid小,继续加
sum += nums[i];
}
}
//如果分完之后组数小于等于m说明,mid还可以更小,即上面思路里说的x还能更小 右区间缩小到mid-1;
if (count <= m) {
ans = Math.min(ans, mid);
right = mid - 1;
} else {
left = mid + 1;
}
}
return ans;
}
/**
* 方法一:动态规划 此方法在牛客网上没全通过,但是是一种正确的结题思路
*/
private static int splitArray(int[] nums, int n, int m) {
int[][] dp = new int[n + 1][m + 1];//dp[i][j]表示前面i个数被分成m个区间中最小的权值
int[] sub = new int[n + 1];
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
dp[i][j] = Integer.MAX_VALUE;
}
}
for (int i = 0; i < n; i++) {
sub[i + 1] = sub[i] + nums[i];//前面i个元素的和
}
dp[0][0] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
for (int k = 0; k < i; k++) {
dp[i][j] = Math.min(dp[i][j], Math.max(dp[k][j - 1], sub[i] - sub[k]));
}
}
}
return dp[n][m];
}
}
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,m;
int Max = INT_MIN,sum=0;
cin>>n>>m;
int a[n];
for(int i=0;i<n;i++)
{
cin>>a[i];
sum+=a[i];
Max = max(Max,a[i]);
}
int left = Max,right = sum;
int cnt;
int t;
while(left<right)
{
cnt = 1;
t = 0;
int mid = (left+right)>>1;
for(int i=0;i<n;i++)
{
if(t+a[i]>mid)
{
cnt++;
t=0;
}
t+=a[i];
}
if(cnt>m)
left=mid + 1;
else right = mid;
}
cout<<left<<endl;
return 0;
} #include <bits/stdc++.h>
using namespace std;
int main()
{
int n,m;
cin>>n>>m;
int a[n],sum=0,b[m],ma=INT_MIN;
for(int i=0;i<n;i++)
{
cin>>a[i];
sum+=a[i];
ma=max(ma,a[i]);
}
int left=ma,right=sum,mid,set, cur;
while(left<right)
{
mid=(left+right)/2;
set = 1;
cur = 0;
for(int i=0;i<n;i++)
{
if(cur+a[i]>mid)
{
set++;
cur = 0;
}
cur+=a[i];
}
if(set > m)
left = mid + 1;
else
right = mid;
}
cout<<left;
return 0;
}
n, m = map(int, input().split()) num = list(map(int, input().split())) left = max(num) right = sum(num) ans = right while left < right: res = 0 cnt = 1 mid = (left+right)//2 for i in range(len(num)): if res+num[i] > mid: cnt += 1 res = num[i] else: res += num[i] if cnt <= m: ans = min(mid, ans) right = mid else: left = mid + 1 print(ans)
#include <bits/stdc++.h>
using namespace std;
int main(){
int n,m,s=0,Max=INT_MIN;
cin>>n>>m;
int a[n];
for(int i=0;i<n;i++){
cin>>a[i];
s += a[i];
Max = max(Max, a[i]);
}
int l=Max,r=s,mid,cnt,t;
while(l<r){
mid = (l+r)/2;
cnt = 1;
t = 0;
for(int i=0;i<n;i++){
if(t+a[i]>mid){
cnt++;
t = 0;
}
t += a[i];
}
if(cnt>m)
l = mid + 1;
else
r = mid;
}
cout<<l<<endl;
return 0;
}
import java.util.*;
public class Main{
//判断nums序列能不能被分割为最大子序列和为part的m段
public static boolean isSuccess(int[] nums,int part,int m){
int cnt=m,partSum=0;
int i,j;
for(i=0;i<nums.length;){
if(cnt<=0) return false;
for(j=i;j<nums.length;j++){
partSum+=nums[j];
if(partSum>part){
partSum-=nums[j];
j--;
cnt--;
break;
}
else {
if(j==nums.length-1){
cnt--;
return true;
}
}
}
i=j+1;
partSum=0;
}
return false;
}
public static void main(String[] args){
Scanner in=new Scanner(System.in);
int sum=0,min=10000,res=0;
int n=in.nextInt(),m=in.nextInt();
in.nextLine();
int[] nums=new int[n];
for(int i=0;i<n;i++){
nums[i]=in.nextInt();
min=Math.min(min,nums[i]);
sum+=nums[i];
}
in.nextLine();
in.close();
if(m==1){
res=sum;
}else{
int resMin=sum/m;
for(int i=resMin;i<=sum-min;i++){
if(isSuccess(nums,i,m)==true){
res=i;
break;
}
}
}
System.out.println(res);
}
}
分成m段,题目要求的结果,其实是要尽可能地把序列均分成每一段之和相等。题目要求的结果肯定至少是所有元素之和/m,这种情况是在所给序列本身就可以正好划分成m段且每一段都为sum/m,比如题目给的样例那样。但是如果改变一下样例的顺序,比如改成4 2 1 5 3,分成3段,答案就不是5了,因为想要把序列分成3段而且每段最大值不大于5,是做不到的。那么就尝试一下5+1=6,看能不能分成3段且每段和小于等于6,可以的:4 2 | 1 5 | 3,假设6也不行,那就再+1,看7行不行,直到找到结果。
tmp = [int(x) for x in input().split()] m,n = tmp[0],tmp[1] data = [int(x) for x in input().split()] def ju(data,split_val): global n cur = 0 cur_val = 0 for i in data: cur_val+=i if cur_val>split_val: cur+=1 cur_val=i if cur>=n: return False return True lo = max(data) hi = sum(data) while lo<=hi: mid = (lo+hi)//2 if ju(data,mid): hi = mid - 1 else: lo = mid + 1 print(lo)由于返回的是lo(st),所以达到要求就动hi(en),这样lo就一定是满足要求的,且是满足要求的最边界的z最大的切分数字,此时切成的数量最小。
if cur>=n:
return False 带着等号。n, k = list(map(int, input().strip().split(" ")))
nums = list(map(int, input().strip().split(" ")))
left, right = max(nums), sum(nums)
res = right
while left <= right:
mid = (left + right)//2
cnt = 1
sum_ = 0
for i in range(n):
if nums[i] + sum_ > mid:
sum_ = nums[i]
cnt += 1
else:
sum_ += nums[i]
if cnt <= k:
res = min(res,mid)
right = mid - 1
else:
left = mid + 1
print(res)