importjava.util.*;publicclassMain{staticint[] arr;staticint[] ans;staticboolean[] st;staticlong res =(long)1e9;staticint n;staticint m;publicstaticvoidmain(String[] args){Scanner sc =newScanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
arr =newint[n];
st =newboolean[n];
ans =newint[m +1];for(int i =0; i < n; i++){
arr[i]= sc.nextInt();}//最后一段肯定是包含arr[n-1]
ans[m]= n;dfs(1);System.out.println(res);}publicstaticvoiddfs(int step){if(step >= m){//Ai <= 1*10^9;一段和最坏的情况下 会>intlong max =-1;//选好的m个分段组合for(int i =0; i < m; i++){long sum =0;for(int j = ans[i]; j < ans[i +1]; j++){
sum += arr[j];}//m段和中的最大值
max =Math.max(sum,max);}//最大值中的最小值
res =Math.min(max,res);return;}//组合枚举for(int i =1; i < n ; i++){if(!st[i]){
ans[step]= i;
st[i]=true;dfs(step +1);
st[i]=false;}}}}
二分解
分析
根据题意: 这个结果的范围是 一组数中的最大值 max 到 这组数的和 sum
max : 当 m = n 时,每个数字为一组 其中的最大值是max; 当 m < n 时 每一段和的最大值肯定是 >= max ( ”=“:当输入的数有0时或者本身就是分段数下的最大值);而最大的情况就是 m = 1时; 全部数为一组也就是 sum。
样例 : 4 2 4 5 1
max = 5 sum = 16
所以答案必在 5 - sum 之间
1当 n = m = 5时:分段和的最大值就是 5;且只有这一种分段的方法
2当 n < m 时; 参照枚举法的分段组合,分段和最大值 >= 5
3当 m = 1 时;答案也就是 16
根据上述分析,枚举出的答案的范围也就随之确定 [max,sum]
而写出的代码如何根据枚举出的答案进行判定呢?
通过分段数 m 和 分段和最大值是否=枚举出的答案 res 进行判定
关键 : 当二分的答案分割的段数越趋近于 m 时,满足题意;数越大分的段越少:数越小分的段越大。
AC解
importjava.util.*;publicclassMain{staticint[] arr;staticlong res =(long)1e9;staticint n;staticint m;publicstaticvoidmain(String[] args){Scanner sc =newScanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
arr =newint[n];//Ai < 10 ^ 9 所以max<int 但 sum可能>int,为了很好比较数据统一使用longlong max =-1;long sum =0;for(int i =0; i < n; i++){
arr[i]= sc.nextInt();//arr[i] : int -> long 自动强制转换
max =Math.max(max,arr[i]);
sum += arr[i];}long l = max;long r = sum;while(l +1< r ){long mid =(l + r)/2;if(check(mid)){
r = mid;}else{
l = mid;}}//特殊情况 l 可以满足if(check(l)){System.out.println(l);}else{System.out.println(r);}}publicstaticbooleancheck(long res){int num =1;long cur =0;for(int i =0; i < n ; i++){//分段和如果 > res,分段 num++,cur = 0->下一分段和if(cur + arr[i]> res){
num ++;
cur =0;}
cur += arr[i];}/*
当 num <= m 时说明比 res 大的数分的段数更少;需要让 num 趋近于 m;所以 return true; r = mid;让 res 变小
当 num > m 时比res小的数的 num 都 > m; 不符合题目要求;我们需要让 num 趋近于 m 所以 return fasle;l = mid;让 res 变大
*/return num <= m;}}