解题思路

这道题可以使用二分查找来解决:

  1. 题目分析:

    • 我们需要找到一个最小值 x,使得所有袋子里的球数都不超过 x
    • 对于每个袋子,如果球数超过 x,需要进行分割操作
    • 分割操作次数不能超过 maxOperations
  2. 二分查找思路:

    • 对于给定的目标值 x,可以计算需要多少次操作才能使所有袋子的球数 ≤ x
    • 如果操作次数 ≤ maxOperations,说明 x 可能还可以更小
    • 如果操作次数 > maxOperations,说明 x 必须更大
  3. 关键点:

    • 二分查找的左边界是 1
    • 右边界是数组中的最大值
    • 对于每个 x,计算需要的操作次数

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution:
def minimumSize(self, nums: List[int], maxOperations: int) -> int:
def check(mid: int) -> bool:
# 计算以mid为目标值时需要的操作次数
operations = sum((num - 1) // mid for num in nums)
return operations <= maxOperations


left, right = 1, max(nums)
# 二分查找
* while left < right:
mid = (left + right) // 2
if check(mid):
# 如果当前的mid可行,mid当前大,可以尝试更小的值
right = mid
else:
# 如果当前的mid不可行,mid 当前太小了,必须尝试更大的值
left = mid + 1
*
return left

代码解释

  1. check(mid)函数:

    • 计算将所有球数控制在mid以下需要的操作次数
    • 对于每个袋子,需要的操作次数是 (num-1) // mid
    • 如果总操作次数 ≤ maxOperations,返回True
  2. 二分查找过程:

    • 初始范围:[1, max(nums)]
    • 每次取中间值mid
    • 根据check(mid)的结果缩小范围
    • 最终找到满足条件的最小值

复杂度分析

  • 时间复杂度:O(n * log M)

    • n 是数组长度
    • M 是数组中的最大值
    • 二分查找需要 log M 次迭代
    • 每次迭代需要 O(n) 时间计算操作次数
  • 空间复杂度:O(1)

    • 只使用了常数额外空间

总结

这是一道典型的二分查找应用题。关键在于:

  1. 将问题转化为判定性问题
  2. 设计合适的判定函数
  3. 正确处理二分查找的边界条件

关键代码详解

1
operations = sum((num - 1) // mid for num in nums)

这行代码计算需要多少次操作才能使所有袋子的球数不超过mid。让我们逐部分分析:

  1. (num - 1) // mid 的含义:

    • 假设一个袋子有 num 个球,目标是将其分成若干个不超过 mid 的小袋
    • 例如:num = 9, mid = 3
    • (9-1) // 3 = 8 // 3 = 2,表示需要2次操作
    • 第一次:9 -> [3,6]
    • 第二次:6 -> [3,3]
    • 最终:[3,3,3]
  2. 为什么是 (num - 1) // mid

    • 如果 num = 7, mid = 3
    • (7-1) // 3 = 6 // 3 = 2 次操作
    • 第一次:7 -> [3,4]
    • 第二次:4 -> [3,1]
    • 最终:[3,3,1]
  3. 整数除法 // 的作用:

    • 向下取整,确保得到最少需要的操作次数
    • 例如:5/2 = 2.5,而 5//2 = 2
  4. 列表推导式 for num in nums

    • 对数组中的每个数字都进行上述计算
  5. sum() 函数:

    • 将所有需要的操作次数加总
    • 得到处理整个数组需要的总操作次数

示例:

1
2
3
4
5
6
nums = [9, 5]
mid = 3

# 对于 9:(9-1)//3 = 8//3 = 2 次操作
# 对于 5:(5-1)//3 = 4//3 = 1 次操作
# 总操作次数:2 + 1 = 3