2025-04-25:移山所需的最少秒数。用go语言,给定一个整数 mountainHeight,代表一座山的高度。
还有一个整数数组 workerTimes,其中每个元素表示对应工人完成单位高度降低所需的时间(单位为秒)。
工人必须同时开始工作以降低山的高度。对于第 i 个工人,如果他降低了高度为 x,那么他所花费的时间是:
workerTimes[i] * (1 + 2 + … + x) = workerTimes[i] * (x * (x + 1) / 2)
举例:
1.降低高度为 1,需要花费 workerTimes[i] 秒;
2.降低高度为 2,需要花费 workerTimes[i] * (1 + 2) 秒;
3.以此类推。
任务是求出所有工人一起合作,把山降到高度 0,所需的最小时间(秒数)。
也就是说,要合理分配工人降低的高度,使得他们各自的工作时间最大值最小,求出这个最小的最大工作时间。
1 <= mountainHeight <= 100000。
1 <= workerTimes.length <= 10000。
1 <= workerTimes[i] <= 1000000。
输入: 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 秒。
题目来自leetcode3296。
T
,在时间 T
内,所有工人合起来能至少降低 mountainHeight
的高度。给定某个时间 T
,判定是否存在一个分配方案使得所有工人在 ≤ T
的时间内,总计降低高度至少 mountainHeight
。
对于工人 i,在时间限制 T
下,能降低的最大高度是多少?
根据时间公式:
时间 = workerTimes[i] × x × (x + 1) / 2 ≤ T
解这个不等式求 x
:
M = T / workerTimes[i]
x
为:midTime
,根据步骤2对每个工人计算其最大降低高度,如果所有工人的降低高度之和 ≥ mountainHeight
,说明可以在该时间或更短时间完成。步骤 1:初始化二分查找范围
计算最大工时:
找出 workerTimes 中的最大工作时间 maxT。
计算最坏情况下某工人单独降低所有高度需要的时间上限:
假设每个工人分担平均高度 h = (mountainHeight - 1) / len(workerTimes) + 1
,
那么最大时间上界大约是:
maxT * h * (h + 1) / 2
步骤 2:进行二分搜索
sort.Search
或经典二分搜索,在区间 [1, maxTime]
之间尝试时间 m
。步骤 3:判断函数
m
,计算每个工人在 m
时间下最大能降低高度:x = floor((√(1 + 8 * (m / workerTimes[i])) - 1) / 2)
totalLoweredHeight
。totalLoweredHeight >= mountainHeight
,函数返回 true
表示时间 m
足够。false
。步骤 4:根据判断结果调整搜索区间
m
足够,尝试缩小区间向左搜索更小时间。步骤 5:最终返回最小满足条件的时间
O(log(maxTime))
,其中 maxTime
是最大时间界限,大致与山高度 * 最大工作时间成正比(但常数相对较大)。n
个工人,计算每个人最大降低高度,复杂度是 O(n)
。O(n log maxTime)
。其中:
n
是工人数,最大 10000,maxTime
最大可能很大,因为 workerTimes[i]
最大可达 10^6,mountainHeight
最大 10^5,但 log
操作使得其可接受。O(n)
空间存储 workerTimes 和少量临时变量。O(n)
。步骤 | 描述 |
---|---|
问题转为判定是否给定时间内可完成任务 | 给定时间 T ,计算每个工人最大降低高度求和是否≥山高 |
二次方程求每个工人在时间限制下最大可降低高度 | 利用公式 x = floor((√(1+8M)-1)/2) ,其中 M = T / workerTimes[i] |
二分搜索时间区间寻找最优时间 | 利用二分法从时间区间中定位最小满足要求的时间 |
每次判定遍历工人计算高度 | 时间复杂度 O(n) |
总时间复杂度 | O(n log(maxTime)) |
总空间复杂度 | O(n) |
这样就实现了高效求解工人同时完成降山任务的最短时间。
package main
import (
"fmt"
"math"
"slices"
"sort"
)
func minNumberOfSeconds(mountainHeight int, workerTimes []int) int64 {
maxT := slices.Max(workerTimes)
h := (mountainHeight-1)/len(workerTimes) + 1
ans := 1 + sort.Search(maxT*h*(h+1)/2-1, func(m int) bool {
m++
leftH := mountainHeight
for _, t := range workerTimes {
leftH -= (int(math.Sqrt(float64(m/t*8+1))) - 1) / 2
if leftH <= 0 {
return true
}
}
return false
})
return int64(ans)
}
func main() {
mountainHeight := 4
workerTimes := []int{2, 1, 1}
result := minNumberOfSeconds(mountainHeight, workerTimes)
fmt.Println(result)
}
# -*-coding:utf-8-*-
import math
def min_number_of_seconds(mountain_height: int, worker_times: list[int]) -> int:
max_t = max(worker_times)
h = (mountain_height - 1) // len(worker_times) + 1
def can_finish(m: int) -> bool:
left_h = mountain_height
for t in worker_times:
# 等效于 Go 代码的:(int(math.Sqrt(float64(m/t*8+1))) - 1) / 2
count = (int(math.isqrt(m // t * 8 + 1)) - 1) // 2
left_h -= count
if left_h <= 0:
return True
return False
left, right = 1, max_t * h * (h + 1) // 2
while left < right:
mid = (left + right) // 2
if can_finish(mid):
right = mid
else:
left = mid + 1
return left
if __name__ == "__main__":
mountain_height = 4
worker_times = [2, 1, 1]
result = min_number_of_seconds(mountain_height, worker_times)
print(result)