首页 > 试题广场 >

分组

[编程题]分组
  • 热度指数:1619 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
牛牛有一个n个数字的序列,现在牛牛想把这个序列分成k段连续段,牛牛想知道分出来的k个连续段的段内数字和的最小值最大可以是多少?
示例1

输入

4,2,[1,2,1,5]

输出

4

说明

有3种分法
[1],[2,1,5],数字和分别为1,8,最小值为1
[1,2][1,5],数字和分别为3,6,最小值为3
[1,2,1],[5]数字和分别为4,5,最小值为4
则最小值的最大值为4

备注:


第一个参数整数n代表序列数字个数
第二个参数整数k代表分出的段数
第三个参数a 包含n个元素代表n个数字

二分“试”答案法

这道题的解题思路,其实是将正面罗列出所有可能性的方式,转化成了“我知道答案的范围和约束条件,但是我不知道具体是哪一个,所以我挨个试一试”的解题方式。在“试一试”的过程中,我们用了二分查找的方式,在答案可能的范围内查找。

约束条件:
  • 假设数组a在k段切分方式下,每段的和为Si,即S1+S2+...+Sk=S(a)
  • 又因为这个和是k个和里最小的,所以满足条件Smin<=S(a)/k
  • 当想要Smin在所有切分方式中最大时,数组的切分会逐渐均匀(即每段和趋于一致),Smin会向S(a)/k逼近。
  • 我们需要做的,就是依次尝试[0, S(a)/k]中的每一个值,看在数组尽量均匀被切分的情况下(即按顺序遍历数组,元素和达到这个值时就切断),能不能被切成K段。
  • 如果能切分,说明我们还可以往更大的数试试。如果不能,就说明太大了,减小答案再试试。

import java.util.*;

public class Solution {
    public int solve (int n, int k, int[] a) {
        if(a.length == 0){
            return 0;
        }
        int sum = 0;
        for(int i = 0; i < n; i ++){
            sum  = sum + a[i];
        }
        if(k == 1){
            return sum;
        }
        int low = 0;
        int high = sum/k;
        while(low <= high){
            int mid = low + (high - low) / 2;
            if(check(a, k, mid)){
                low = mid + 1;
            }else{
                high = mid - 1;
            }
        }
        return (low+high)/2;
    }
    public boolean check(int a[], int k, int min_max_sum){
        int count = 0;
        int sum = 0;
        for(int j = 0; j < a.length; j++){
            sum += a[j];
            if(sum >= min_max_sum){
                sum = 0;
                count += 1;
                if(count >= k){
                    return true;
                }
            }
        }
        return false;
    }
}


发表于 2021-04-02 01:21:16 回复(0)

问题信息

难度:
1条回答 7316浏览

热门推荐

通过挑战的用户

查看代码