给你一个下标从 0 开始的数组 nums
,它含有 n
个非负整数。
每一步操作中,你需要:
- 选择一个满足
1 <= i < n
的整数i
,且nums[i] > 0
。 - 将
nums[i]
减 1 。 - 将
nums[i - 1]
加 1 。
你可以对数组执行 任意 次上述操作,请你返回可以得到的 nums
数组中 最大值 最小 为多少。
示例 1:
输入:nums = [3,7,1,6] 输出:5 解释: 一串最优操作是: 1. 选择 i = 1 ,nums 变为 [4,6,1,6] 。 2. 选择 i = 3 ,nums 变为 [4,6,2,5] 。 3. 选择 i = 1 ,nums 变为 [5,5,2,5] 。 nums 中最大值为 5 。无法得到比 5 更小的最大值。 所以我们返回 5 。
示例 2:
输入:nums = [10,1] 输出:10 解释: 最优解是不改动 nums ,10 是最大值,所以返回 10 。
提示:
n == nums.length
2 <= n <= 105
0 <= nums[i] <= 109
提示 1
Try a binary search approach.
提示 2
Perform a binary search over the minimum value that can be achieved for the maximum number of the array.
提示 3
In each binary search iteration, iterate through the array backwards, greedily decreasing the current element until it is within the limit.
解法:二分答案
class Solution {
public int minimizeArrayValue(int[] nums) {
int max = 0;
int min = 0;
for (int num : nums) {
max = Math.max(max, num);
min = Math.min(min, num);
}
// 闭区间写法
int left = min;
int right = max;
while (left <= right) {
// 循环不变量:
// check(right + 1), nums[0] + extra <= x, 划分的最大值偏大,应该缩小右边界
// check(left - 1), nums[0] + extra > x, 划分的最大值偏小,应该扩大左边界
int mid = left + (right - left) / 2;
if (check(nums, mid)) {
right = mid - 1;
} else {
left = mid + 1;
}
}
// left == right + 1;
return left;
// 左闭右开区间写法
// int left = min;
// int right = max + 1;
// while (left < right) {
// // 循环不变量:
// // check(right), nums[0] + extra <= x, 划分的最大值偏大,应该缩小右边界
// // check(left - 1), nums[0] + extra > x, 划分的最大值偏小,应该扩大左边界
// int mid = left + (right - left) / 2;
// if (check(nums, mid)) {
// right = mid;
// } else {
// left = mid + 1;
// }
// }
// // left == right;
// return left;
// 左开右闭区间写法
// int left = min - 1;
// int right = max;
// while (left < right) {
// // 循环不变量:
// // check(right + 1), nums[0] + extra <= x, 划分的最大值偏大,应该缩小右边界
// // check(left), nums[0] + extra > x, 划分的最大值偏小,应该扩大左边界
// int mid = left + (right - left + 1) / 2;
// if (check(nums, mid)) {
// right = mid - 1;
// } else {
// left = mid;
// }
// }
// // left == right;
// return right + 1;
// 开区间写法
// int left = min -1;
// int right = max + 1;
// while (left + 1 < right) {
// // 循环不变量:
// // check(right), nums[0] + extra <= x, 划分的最大值偏大,应该缩小右边界
// // check(left), nums[0] + extra > x, 划分的最大值偏小,应该扩大左边界
// int mid = left + (right - left) / 2;
// if (check(nums, mid)) {
// right = mid;
// } else {
// left = mid;
// }
// }
// // left + 1 == right;
// return right;
}
private boolean check(int[] nums, int x) {
// 0 <= nums[i] <= 10^9,防止整型溢出,用long
long extra = 0;
for (int i = nums.length - 1; i >= 1; i--) {
// extra表示使得当前项<=x 时需要需要减去的值,即加到前一项的值
if (nums[i] + extra > x) {
extra = nums[i] + extra - x;
} else {
extra = 0;
}
}
return nums[0] + extra <= x;
}
}
复杂度分析
时间复杂度:O(nlogU),其中 n 为 nums 的长度,U=max(nums) - min(nums)。
空间复杂度:O(1)。
标签:2439,right,nums,int,min,mid,最小化,LeetCode,left From: https://blog.csdn.net/m0_56090828/article/details/137630793