原文链接: https://leetcode-cn.com/problems/find-the-smallest-divisor-given-a-threshold
英文原文
Given an array of integers nums
and an integer threshold
, we will choose a positive integer divisor
, divide all the array by it, and sum the division's result. Find the smallest divisor
such that the result mentioned above is less than or equal to threshold
.
Each result of the division is rounded to the nearest integer greater than or equal to that element. (For example: 7/3 = 3
and 10/2 = 5
).
It is guaranteed that there will be an answer.
Example 1:
Input: nums = [1,2,5,9], threshold = 6 Output: 5 Explanation: We can get a sum to 17 (1+2+5+9) if the divisor is 1. If the divisor is 4 we can get a sum of 7 (1+1+2+3) and if the divisor is 5 the sum will be 5 (1+1+1+2).
Example 2:
Input: nums = [44,22,33,11,1], threshold = 5 Output: 44
Example 3:
Input: nums = [21212,10101,12121], threshold = 1000000 Output: 1
Example 4:
Input: nums = [2,3,5,7,11], threshold = 11 Output: 3
Constraints:
1 <= nums.length <= 5 * 104
1 <= nums[i] <= 106
nums.length <= threshold <= 106
中文题目
给你一个整数数组 nums
和一个正整数 threshold
,你需要选择一个正整数作为除数,然后将数组里每个数都除以它,并对除法结果求和。
请你找出能够使上述结果小于等于阈值 threshold
的除数中 最小 的那个。
每个数除以除数后都向上取整,比方说 7/3 = 3 , 10/2 = 5 。
题目保证一定有解。
示例 1:
输入:nums = [1,2,5,9], threshold = 6 输出:5 解释:如果除数为 1 ,我们可以得到和为 17 (1+2+5+9)。 如果除数为 4 ,我们可以得到和为 7 (1+1+2+3) 。如果除数为 5 ,和为 5 (1+1+1+2)。
示例 2:
输入:nums = [2,3,5,7,11], threshold = 11 输出:3
示例 3:
输入:nums = [19], threshold = 5 输出:4
提示:
1 <= nums.length <= 5 * 10^4
1 <= nums[i] <= 10^6
nums.length <= threshold <= 10^6
通过代码
高赞题解
思路:首先要读题,读懂题意是关键。题目要我们求的是除数。
1、根据题目说明:
请你找出能够使上述结果小于等于阈值
threshold
的除数中 最小 的那个。
再观察数据范围:
1 <= nums.length <= 5 * 10^4
1 <= nums[i] <= 10^6
nums.length <= threshold <= 10^6
明显可以使用二分查找。
2、于是思考除数最大是多少,最小是多少。
- 最大是数组中最大的那个数,因为除数如果再大,整除以后每个数都得 1(上取整的缘故);
- 最小可以是 1。
我一开始以为最小就是数组中最小的那个数字,后来提交以后,发现测试用例:
int[] nums = {91, 41, 78, 86, 8};
int threshold = 114;
不能通过。于是才知道,原来 threshold
可以很大,因此除数的最小值可以让它是 $1$ 。因为二分查找很快,除数的下限小一点是可以的,只要包含目标值就行。
3、写 if
分支的时候,就根据题目的意思写即可。
选择一个正整数作为除数,然后将数组里每个数都除以它,并对除法结果求和。
思考的关键是:什么时候不是解?
题目说:
找出能够使上述结果小于等于阈值
threshold
的除数中 最小 的那个。
因此:和严格大于阈值 threshold
的除数,一定不是解。根据“减而治之”的策略,定位这个除数。
参考代码 1:
[]public class Solution { public int smallestDivisor(int[] nums, int threshold) { // 先找数组中的最大值,用最大值作为除数,除完以后和最小 int maxVal = 1; for (int num : nums) { maxVal = Math.max(maxVal, num); } // 注意:最小值是 1,因为 threshold 可以很大 int left = 1; int right = maxVal; while (left < right) { int mid = (left + right) >>> 1; if (calculateSum(nums, mid) > threshold) { // sum 大于阈值一定不是解,说明除数选得太小了 // 下一轮搜索区间是 [mid + 1, right] // (把下一轮搜索区间写出来,边界选择就不会错) left = mid + 1; // 边界是 left = mid + 1 ,中间数不用上取整 } else { right = mid; } } return left; } /** * @param nums * @param divisor * @return 数组中各个元素与 divisor 相除的结果(向上取整)之和 */ private int calculateSum(int[] nums, int divisor) { int sum = 0; for (int num : nums) { sum += num / divisor; // 注意:不能整除的时候,需要向上取整 if (num % divisor != 0) { sum++; } } return sum; } }
[]from typing import List class Solution: def smallestDivisor(self, nums: List[int], threshold: int) -> int: left = 1 right = max(nums) while left < right: mid = (left + right) >> 1 if sum([(num + mid - 1) // mid for num in nums]) > threshold: left = mid + 1 else: right = mid return left
[]#include <iostream> #include <vector> using namespace std; class Solution { private: int calculateSum(vector<int> &nums, int divisor) { int sum = 0; for (int num : nums) { sum += (num + divisor - 1) / divisor; } return sum; } public: int smallestDivisor(vector<int> &nums, int threshold) { int maxVal = 1; for (int num : nums) { maxVal = max(maxVal, num); } int left = 1; int right = maxVal; while (left < right) { int mid = left + (right - left) / 2; if (calculateSum(nums, mid) > threshold) { left = mid + 1; } else { right = mid; } } return left; } };
说明:上取整还可以这样写,这是个小技巧,记住就可以了。
[]sum += (num + divisor - 1) / divisor;
复杂度分析:
- 时间复杂度:$O(N \log \max(nums))$,这里 $N$ 是数组的长度,每一次二分都执行了边界判断函数,都得遍历一遍数组;
这里感谢 @copyreadmachine 朋友帮我纠正了时间复杂度。
- 空间复杂度:$O(1)$。
统计信息
通过次数 | 提交次数 | AC比率 |
---|---|---|
7426 | 17685 | 42.0% |
提交历史
提交时间 | 提交结果 | 执行时间 | 内存消耗 | 语言 |
---|