Weighted Interval Scheduling === ### 題目 ![image](https://hackmd.io/_uploads/H1kMPFtDp.png) ### 解法 定義 OPT(i) 為前 i 個活動可產生的最大權重。 定義 $a_i$ 為第 i 項活動,$w_i$ 為第 i 項活動的權重,$s_i$ 為第 i 項活動的開始時間,$f_i$ 為第 i 項活動的結束時間。 定義 $q_i$ 為排除掉和 $a_i$ 衝突的活動後的最大的 index j 使得 $s_i \ge f_j$,即結束時間最接近 $a_i$ 的活動編號。 1. 將每個活動依照結束時間由小到大排序 2. 計算 $q_i$ for all i 3. 從最後一個活動 $a_i$ 開始排,考慮排入或不排入 Case 1 : 排入活動 $a_i$,需先扣除掉和 $a_i$ 時間衝突的活動,再求出最大的 index j 使得 $s_i \ge f_j$,所以 $OPT(i) = OPT(f(i)) + w_i$ Case 2 : 不排入活動 $a_i$,則 $OPT(i) = OPT(i - 1)$ 故 $OPT(i) = \left \{ \begin{array}{rcl} 0 & if\ i = 0 \\ max\{OPT(q_i) + w_i, OPT(i - 1)\} & otherwise\end{array}\right.$ ### Time 1. 排序花 O(nlogn) 2. 計算 $q_i$ 可利用 **Binary Search** $\Rightarrow O(nlogn)$ 3. 利用 Bottom-up DP 的方式可在 O(n) 內算完 OPT(i) for all i 故整體時間為 O(nlogn) ### 實作 ```= #include <iostream> #include <algorithm> using namespace std; struct Job { int start, finish, weight; }; bool cmp(Job s1, Job s2) { return s1.finish < s2.finish; } int binarySearch(Job jobs[], int index) { int low = 0, high = index - 1; // 活動要在 a_i 之前,所以要 index - 1 while (low <= high) { int mid = (low + high) / 2; if (jobs[mid].finish <= jobs[index].start) { // 這裡有微幅修改 BinarySearch // 先檢查 mid 的下一個有沒有滿足 finish(mid + 1) <= start(index), // 如果沒有滿足,代表 mid 就是我們要找的最大 index,就可以不用繼續二分搜尋了 if (jobs[mid + 1].finish <= jobs[index].start) low = mid + 1; else return mid; } else high = mid - 1; } return -1; // 沒找到,時間都撞到 } int findMaxWeight(Job arr[], int n) { sort(arr, arr + n, cmp); int *table = new int[n]; table[0] = arr[0].weight; for (int i = 1; i < n; i++) { int includeWeight = arr[i].weight, q = binarySearch(arr, i); if (q != -1) includeWeight += table[q]; table[i] = max(includeWeight, table[i-1]); } int result = table[n-1]; delete[] table; return result; } int main() { Job arr[] = {{3, 10, 20}, {1, 2, 50}, {6, 19, 100}, {2, 100, 200}}; int n = sizeof(arr) / sizeof(arr[0]); cout << "Optimal weight is " << findMaxWeight(arr, n); } ```