題目描述:
有 n 個工作,第 i 個工作在時間 [startTime[i], endTime[i]) 執行並獲得 profit[i] 的利潤。選擇若干工作(時間不重疊,結束時間等於下一個開始時間視為不衝突),使總利潤最大。
解題思路:DP + 二分搜尋:
將工作依結束時間排序,定義 dp[i] 為考慮前 i 個工作(0 表示不選任何工作)能獲得的最大利潤。
對第 i 個工作(1-indexed),有兩種選擇:
dp[i] = dp[i-1]dp[i] = dp[k] + profit[i-1]
dp[i] = max(dp[i-1], dp[k] + profit[i-1])
實作細節:
endTimes 陣列(記錄前 i 個工作的結束時間),供二分搜尋使用。endTimes[k-1] <= startTime[i-1],即 upper_bound 之前。時間複雜度:O(n log n) — 排序 O(n log n);每個工作做一次二分搜尋 O(log n),共 O(n log n)。整體由排序主導。
空間複雜度:O(n) — jobs 陣列、dp 陣列、ends 陣列各佔 O(n) 空間。
1. Merge (startTime, endTime, profit) into jobs array; sort by endTime. 2. Initialize dp[0..n] = 0, ends = [] (stores end times for binary search). 3. For i from 0 to n-1: a. (end, start, p) = jobs[i] b. k = upper_bound(ends, start) // # jobs whose endTime <= start c. dp[i+1] = max(dp[i], dp[k] + p) d. Append end to ends 4. Return dp[n].
map 代替 dp 陣列(程式碼更精簡):用 map<int,int> 以結束時間為 key 儲存最大利潤,每次利用 upper_bound 查詢,最後取最大值。邏輯等價,但常數稍大。
記憶化遞迴(Top-Down DP):先按結束時間排序,定義 solve(i) = 從第 i 個工作開始能獲得的最大利潤;對每個工作選擇選或不選,用二分搜尋跳到下一個不衝突的工作。時空複雜度相同,遞迴版本在面試中更直觀但需額外的遞迴棧空間。
線段樹 / BIT:若工作時間為離散整數且範圍不大,可以用線段樹做區間最大值查詢,避免排序依賴,但實作較複雜,不適合作為首選解法。
upper_bound 找「最後一個結束時間 ≤ start」等同於 lower_bound(start+1) - 1。請解釋這兩種寫法在語義上是否完全等價,有無邊界條件的差異?