Loading...

这道题告诉你-有时候-反着想-就对了

在这里插入图片描述

求解思路

这道题与其直接想"怎么分割数组",不如先问"如果我规定每个子数组的和不能超过某个值 x,那最少需要分成几部分?"

通过这个反向思考,我们可以用二分搜索来找答案。

想象一下调节音量的过程,我们在 0 到数组总和之间不断试探一个"限制值",每次尝试时都检查:

在这个限制下能否把数组分成不超过 k 个部分。

如果可以做到,说明这个限制还能再小一点,我们就往更小的方向试;

如果做不到,说明限制太严格了,我们就往更大的方向试。

这样一步步逼近,最终就能找到那个"恰好能分成 k 个部分"的最小限制值,这个值就是答案。

代码实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public static int splitArray(int[] nums, int k) {
    // 计算数组总和,这是二分搜索的右边界
    long sum = 0;
    for (int num : nums) {
        sum += num;
    }
    
    long ans = 0;
    // 在 [0, sum] 区间进行二分搜索
    for (long l = 0, r = sum, m, need; l <= r;) {
        // 计算中点
        m = l + ((r - l) >> 1);
        
        // 关键:如果限制每部分和不超过 m,需要分成几个部分?
        need = f(nums, m);
        
        if (need <= k) {
            // 如果分成的部分数 <= k,说明 m 可行
            // 但可能还能更小,继续在左半边找
            ans = m;
            r = m - 1;
        } else {
            // 如果需要分成的部分 > k,说明 m 太小了
            // 需要增大限制,在右半边找
            l = m + 1;
        }
    }
    return (int) ans;
}

// 辅助函数:给定限制 limit,计算最少需要分成几个部分
public static int f(int[] arr, long limit) {
    int parts = 1;  // 至少需要 1 个部分
    int sum = 0;    // 当前部分的累加和
    
    for (int num : arr) {
        // 如果单个元素就超过限制,无法分割
        if (num > limit) {
            return Integer.MAX_VALUE;
        }
        
        // 如果加上当前元素会超过限制
        if (sum + num > limit) {
            parts++;      // 开启新的部分
            sum = num;    // 当前元素作为新部分的起始
        } else {
            sum += num;   // 继续累加到当前部分
        }
    }
    return parts;
}

如果觉得有帮助,欢迎点赞、关注、转发~

最后更新于 2026-04-05 17:35:33
Code Road Record