Medium
题目描述
给定一个整数 mountainHeight,表示山峰的高度。
还给定一个整数数组 workerTimes,表示工人的工作时间(以秒为单位)。
工人们同时工作来降低山峰的高度。对于工人 i:
- 要将山峰的高度降低 x,需要
workerTimes[i] + workerTimes[i] * 2 + ... + workerTimes[i] * x秒。例如:- 要将山峰的高度降低 1,需要
workerTimes[i]秒。 - 要将山峰的高度降低 2,需要
workerTimes[i] + workerTimes[i] * 2秒,以此类推。
- 要将山峰的高度降低 1,需要
返回一个整数,表示工人们使山峰高度变为 0 所需的最小秒数。
示例 1:
输入:mountainHeight = 4, workerTimes = [2,1,1]
输出:3
解释:
山峰高度可以降低到 0 的一种方式是:
- 工人 0 降低高度 1,耗时 workerTimes[0] = 2 秒。
- 工人 1 降低高度 2,耗时 workerTimes[1] + workerTimes[1] * 2 = 3 秒。
- 工人 2 降低高度 1,耗时 workerTimes[2] = 1 秒。
由于他们同时工作,所需的最小时间是 max(2, 3, 1) = 3 秒。
示例 2:
输入:mountainHeight = 10, workerTimes = [3,2,2,4]
输出:12
示例 3:
输入:mountainHeight = 5, workerTimes = [1]
输出:15
约束条件:
1 <= mountainHeight <= 10^51 <= workerTimes.length <= 10^41 <= workerTimes[i] <= 10^6
解题思路
这道题的核心思路是使用二分搜索 + 贪心算法来求解最小时间。
解题思路:
关键观察:工人 i 降低高度 x 所需的时间为
workerTimes[i] * (1 + 2 + ... + x) = workerTimes[i] * x * (x + 1) / 2。这是一个关于 x 的二次函数。二分搜索:我们可以二分搜索答案的时间 t。对于每个时间 t,检查是否能在时间 t 内将山峰高度降为 0。
检查函数:给定时间 t,对每个工人 i,我们需要找到在时间 t 内该工人能降低的最大高度。这需要求解不等式:
workerTimes[i] * x * (x + 1) / 2 ≤ t,解得 x 的最大整数值。贪心策略:每个工人都尽可能多地降低高度,然后检查总的降低高度是否不小于 mountainHeight。
优化技巧:
- 使用数学公式求解二次不等式,避免线性搜索
- 二分搜索的上界可以设为单个工人完成所有工作所需的时间
时间复杂度分析:
- 二分搜索:O(log(上界))
- 每次检查:O(n),其中 n 是工人数量
- 总体:O(n * log(上界))
代码实现
class Solution {
public:
long long minNumberOfSeconds(int mountainHeight, vector<int>& workerTimes) {
long long left = 1;
long long right = 1LL * mountainHeight * (mountainHeight + 1) / 2 * (*min_element(workerTimes.begin(), workerTimes.end()));
while (left < right) {
long long mid = left + (right - left) / 2;
if (canFinish(mid, mountainHeight, workerTimes)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
private:
bool canFinish(long long time, int mountainHeight, vector<int>& workerTimes) {
long long totalReduction = 0;
for (int workerTime : workerTimes) {
long long maxHeight = getMaxHeight(time, workerTime);
totalReduction += maxHeight;
if (totalReduction >= mountainHeight) {
return true;
}
}
return totalReduction >= mountainHeight;
}
long long getMaxHeight(long long time, int workerTime) {
// Solve: workerTime * x * (x + 1) / 2 <= time
// x^2 + x <= 2 * time / workerTime
long long limit = 2 * time / workerTime;
long long x = (long long)sqrt(limit);
// Check x and x+1 to find the exact answer
while (x * (x + 1) <= limit) x++;
while (x * (x + 1) > limit) x--;
return x;
}
};
class Solution:
def minNumberOfSeconds(self, mountainHeight: int, workerTimes: List[int]) -> int:
def can_finish(time):
total_reduction = 0
for worker_time in workerTimes:
max_height = get_max_height(time, worker_time)
total_reduction += max_height
if total_reduction >= mountainHeight:
return True
return total_reduction >= mountainHeight
def get_max_height(time, worker_time):
# Solve: worker_time * x * (x + 1) / 2 <= time
# x^2 + x <= 2 * time / worker_time
limit = 2 * time // worker_time
x = int(limit ** 0.5)
# Check x and x+1 to find the exact answer
while x * (x + 1) <= limit:
x += 1
while x * (x + 1) > limit:
x -= 1
return x
left = 1
right = mountainHeight * (mountainHeight + 1) // 2 * min(workerTimes)
while left < right:
mid = (left + right) // 2
if can_finish(mid):
right = mid
else:
left = mid + 1
return left
public class Solution {
public long MinNumberOfSeconds(int mountainHeight, int[] workerTimes) {
long left = 1;
long right = (long)mountainHeight * (mountainHeight + 1) / 2 * workerTimes.Min();
while (left < right) {
long mid = left + (right - left) / 2;
if (CanFinish(mid, mountainHeight, workerTimes)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
private bool CanFinish(long time, int mountainHeight, int[] workerTimes) {
long totalReduction = 0;
foreach (int workerTime in workerTimes) {
long maxHeight = GetMaxHeight(time, workerTime);
totalReduction += maxHeight;
if (totalReduction >= mountainHeight) {
return true;
}
}
return totalReduction >= mountainHeight;
}
private long GetMaxHeight(long time, int workerTime) {
// Solve: workerTime * x * (x + 1) / 2 <= time
// x^2 + x <= 2 * time / workerTime
long limit = 2 * time / workerTime;
long x = (long)Math.Sqrt(limit);
// Check x and x+1 to find the exact answer
while (x * (x + 1) <= limit) x++;
while (x * (x + 1) > limit) x--;
return x;
}
}
var minNumberOfSeconds = function(mountainHeight, workerTimes) {
function canFinish(time) {
let totalReduction = 0;
for (let workerTime of workerTimes) {
let maxHeight = getMaxHeight(time, workerTime);
totalReduction += maxHeight;
if (totalReduction >= mountainHeight) {
return true;
}
}
return totalReduction >= mountainHeight;
}
function getMaxHeight(time, workerTime) {
// Solve: workerTime * x * (x + 1) / 2 <= time
// x^2 + x <= 2 * time / workerTime
let limit = Math.floor(2 * time / workerTime);
let x = Math.floor(Math.sqrt(limit));
// Check x and x+1 to find the exact answer
while (x * (x + 1) <= limit) x++;
while (x * (x + 1) > limit) x--;
return x;
}
let left = 1;
let right = mountainHeight * (mountainHeight + 1) / 2 * Math.min(...workerTimes);
while (left < right) {
let mid = Math.floor((left + right) / 2);
if (canFinish(mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n × log(H²×T)) | n 为工人数量,H 为山峰高度,T 为最小工作时间 |
| 空间复杂度 | O(1) | 只使用常数额外空间 |