🔗 🟡 2187. Minimum Time to Complete Trips 1641

題意

給定一個陣列 timetime,其中 time[i]time[i] 表示第 ii 輛公車完成 一次旅程 所需的時間。

每輛公車可以 連續 完成多趟旅程,也就是說,一輛公車當前旅程完成後,可以 立即開始 下一趟旅程。每輛公車 獨立 運行,也就是說可以同時有多輛公車在運行且互不影響。

給定一個整數 totalTripstotalTrips,表示所有公車總共應完成的旅程數。返回完成 至少 totalTripstotalTrips 旅程所需的 最少 時間。

約束條件

  • 1time.length1051 \leq \text{time.length} \leq 10^5
  • 1time[i],totalTrips1071 \leq \text{time[i]}, \text{totalTrips} \leq 10^7

首先不難發現,當時間 tt 越大,所有公車完成的旅程數越多,因此答案具有 單調性 ,可以對答案進行 二分搜尋(Binary Search) 來找到最小的時間。

因此我們可以定義一個函數 check(t) 來檢查在時間 tt 內是否能完成至少 totalTripstotalTrips 次旅程。對於第 ii 輛公車,其在時間 tt 內最多能完成 ttimei\lfloor \frac{t}{time_i} \rfloor 次旅程,因此我們只需要將所有公車的完成旅程數相加後,與 totalTripstotalTrips 比較即可。若 (i=0n1ttimei)totalTrips\displaystyle (\sum_{i=0}^{n-1} \lfloor \frac{t}{time_i} \rfloor) \geq totalTrips,則代表在時間 tt 內能完成至少 totalTripstotalTrips 次旅程,返回 TrueTrue;否則返回 FalseFalse

接著確定二分搜尋的範圍,由於至少需要 11 單位時間完成一次旅程,因此左界為 11;由於最多需要 time\text{time} 陣列中的最小值乘以 totalTripstotalTrips 才能完成所有旅程,因此可以令右界為 min(time)×totalTrips\min(\text{time}) \times totalTrips

這裡使用 閉區間 的寫法,因此 while 迴圈的條件為 left <= right。在二分搜尋結束後,rightright 會指向最大的不滿足條件的時間,而 leftleft 會指向最小的滿足條件的時間,因此答案為 leftleft

複雜度分析

  • 時間複雜度:O(log(D))\mathcal{O}(\log(D)),其中 D=min(time)×totalTripsD = \min(\text{time}) \times totalTrips 為二分搜尋的範圍
  • 空間複雜度:O(1)\mathcal{O}(1)
1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution:
def minimumTime(self, time: List[int], totalTrips: int) -> int:
def check(t):
return sum(t // x for x in time) >= totalTrips

left, right = 1, min(time) * totalTrips
while left <= right:
mid = (left + right) // 2
if check(mid):
right = mid - 1
else:
left = mid + 1
return left
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public:
long long minimumTime(vector<int>& time, int totalTrips) {
function<bool(long long)> check = [&](long long t) -> bool {
long long sum = 0;
for (int x : time) {
sum += t / x;
}
return sum >= totalTrips;
};
long long left = 1;
long long right = (long long) *min_element(time.begin(), time.end()) * totalTrips;
while (left <= right) {
long long mid = (left + right) / 2;
if (check(mid)) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}
};

寫在最後

Cover photo is remixed from @吃肥皂泡泡, thanks for their work!