=========================================================================== CSC 373H / L0501 Lecture Summary for Week 7 Fall 2006 =========================================================================== Scheduling jobs with deadlines, durations, and profits. - Input: Jobs (d_1,t_1,g_1), ..., (d_n,t_n,g_n), where d_i = deadline, t_i = duration, g_i = gain (profit) for job i. Output: A feasible schedule with maximum profit. "Schedule" = list of start times for each job, S(1),S(2),...,S(n), where S(i) = start time for job i (or -1 if job i not scheduled). "Feasible schedule": each job finishes by its deadline (i.e., S(i)+t_i <= d_i), no two jobs overlap. - Step 0: As before sort jobs by deadline (d_1 <= d_2 <= ... <= d_n). Consider optimal schedule S where k = largest job number scheduled and t = time by which all jobs finish. If job k is not scheduled last, then we can move job k to finish at time t (since d_k is largest deadline) and, if necessary, move other jobs earlier to make room. This gives optimal schedule where k is scheduled last. Moreover, rest of jobs must form optimal schedule of jobs from 1,...,k-1 that finish by time t-t_k (start time for job k). - Step 1: Define array. A[i,t] = max profit from jobs 1,2,...,i finishing by time t for 0 <= i <= n, 0 <= t <= d_n. - Step 2: Write recurrence. A[0,t] = 0 A[i,t] = A[i-1,t] if t < t_i A[i,t] = max{ A[i-1,t], g_i + A[i-1,min(t,d_i)-t_i] } if t >= t_i because job i must finish by time min(t,d_i) so its latest start time is min(t,d_i)-t_i, and we consider both possibilities (schedule job i or not). - Step 3: Compute values bottom-up. for t := 0 to d_n: A[0,t] := 0 for i := 1 to n: for t := 0 to d_n: A[i,t] := A[i-1,t] t' := min(t,d_i)-t_i // latest start time for job i if t' >= 0 and g_i + A[i-1,t'] > A[i,t]: A[i,t] := g_i + A[i-1,t'] Runtime? Theta(n d_n). This is NOT polynomial time because it depends on the numerical value of an input parameter, not just on the size of the input. - Step 4: Find optimal solution. t := d_n for i := n downto 1: if A[i,t] = A[i-1,t]: // don't schedule job i S(i) := -1 else: // schedule job i as late as possible S(i) := min(t,d_i) - t_i t := S(i) Runtime? Theta(n), in addition to Step 3. - Example: input: A: i\t| 0 1 2 3 4 d: 2 2 3 4 -------------------- t: 2 1 2 2 0 | 0 0 0 0 0 g: 3 4 2 3 1 | 0 0 3 3 3 2 | 0 4 4 4 4 schedule: 3 | 0 4 4 6 6 S: -1 1 -1 2 4 | 0 4 4 6 7 RNA secondary structure. - Input: A sequence of bases b_1,b_2,...,b_n, each b_i in {A,C,G,U}. Output: A sequence of pairs (i_1,j_1),(i_2,j_2),...,(i_k,j_k) where k is as large as possible and: . for each pair (i,j), 1 <= i < j-4 <= n-4; . for each pair (i,j), {b_i,b_j} = {A,U} or {C,G}; . no index is repeated (i.e., all i's and j's are distinct); . no two pairs "cross", i.e., for all pairs (i,j) and (i',j'), it is NOT the case that i < i' < j < j'. - Step 1: OPT[i,j] = max number of pairs on b_i,...,b_j - Step 2: OPT[i,j] = 0 for all i >= j-4 OPT[i,j] = max of: OPT[i,j-1], 1 + OPT[i+1,j-1] if b_i matches b_j, max( 1 + OPT[i,t-1] + OPT[t+1,j-1] ) for all t in [i+1,j-5] such that b_t matches b_j The first term is the best possible if b_j is unmatched. The second term is the best way to match b_i with b_j (if they match). The last term covers all other possible ways that b_j could be matched, and the best possible answer in each case. - Step 3: Observations: OPT[i,j] depends on previous values "below and to the left" also, we don't need to store OPT[i,j] for values of i >= n-4 or values of j <= 5. for i := n-5 downto 1: for j := i to i+4: OPT[i,j] := 0 for j := i+5 to n: OPT[i,j] := OPT[i,j-1] if b_i matches b_j and OPT[i,j] < 1 + OPT[i+1,j-1]: OPT[i,j] := 1 + OPT[i+1,j-1] for t := i+1 to j-5: if b_t matches b_j and OPT[i,j] < 1 + OPT[i,t-1] + OPT[t+1,j-1]: OPT[i,j] := 1 + OPT[i,t-1] + OPT[t+1,j-1] Example: see page 277 of textbook.