=========================================================================== CSC 373H Lecture Summary for Week 5 Winter 2006 =========================================================================== Interval scheduling with profits: [6.1] - Just like activity (interval) scheduling but each activity (interval) has a "profit" and we want schedule with maximum profit. More precisely: Input: Activities (s_1,f_1,p_1), ..., (s_n,f_n,p_n) where s_i = start time, f_i = finish time, p_i = profit. Output: Subset of activities S subset {1,2,...,n} such that no activities in S overlap and profit(S) is maximal. - Greedy doesn't work, no matter how we sort. - Step 0: Subproblems consist of subsets of activities to choose from, but no easy way to characterize these using constant number of parameters. Trick: Sort activities by finish time, as before (f_1 <= ... <= f_n). Consider optimal schedule S, and last activity scheduled in S, say k. Then k must be larger than index of all other jobs scheduled (because of sorting order) and rest of schedule must consist of optimal way to schedule the other activities. - Step 1: Define array A[i] = max profit from scheduling activities 1,2,...,i - Step 2: Write recurrence A[0] = 0 A[i] = max( A[i-1], p_i + A[j] ) where j = largest index <= i-1 such that f_j <= s_i because either job i is not required to get an optimal schedule (in which case A[i] = A[i-1]) or job i is required (in which case A[i] = p_i + A[j]). - Step 3: Bottom-up algorithm A[0] := 0 for i := 1 to n do A[i] := A[i-1] find largest j <= i-1 such that f_j <= s_i if j > 0 and A[i] < p_i + A[j] then A[i] := p_i + A[j] - Step 4: Compute optimal answer S := {} i := n while i > 0 do if A[i] = A[i-1] then // don't schedule job i i := i - 1 else // schedule job i S := S U {i} i := largest j <= i-1 such that f_j <= s_i return S - Runtime? O(n^2) (for each job, may have to look at all previous ones to find j) -- if information about the values of j is precomputed, then only O(n). Scheduling jobs with deadlines, durations, and profits. - Input: Jobs (d_1,t_1,p_1), ..., (d_n,t_n,p_n), where d_i = deadline, t_i = duration, p_i = 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 < d_i A[i,t] = max{ A[i-1,t], p_i + A[i-1, min(t,d_i)-t_i] } if t >= d_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 do A[0,t] := 0 for i := 1 to n do for t := 0 to d_n do A[i,t] := A[i-1,t] t' := min(t,d_i)-t_i // latest start time for job i if t' >= 0 and p_i + A[i-1,t'] > A[i,t] then A[i,t] := p_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 do if A[i,t] = A[i-1,t] then // 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 All-pairs Shortest Paths. [Old CSC364 notes.] - Input: Directed graph G=(V,E) with arbitrary edge weights w(e), but no negative weight cycles. Output: For each pair u,v in V, weight of a "shortest" (smallest weight) path from u to v. - Greedy approach doesn't work: this would be Dijkstra's algorithm, which works fine for nonnegative weights but doesn't give the correct answer if any weights are negative. - Step 0: Consider a shortest path P from u to v. Since G contains no cycle with negative weight, P must be simple (no cycles). If P contains more than 1 edge, let w be any intermediary vertex on P ("intermediary" means "other than the endpoints"). Let P_uw be the part of P from u to w and P_wv be the part of P from w to v. Claim: P_uw is a shortest path from u to w. Proof: By contradiction: suppose there is a path P' shorter than P_uw. If P' shares no intermediate node with P_wv, then P' followed by P_wv is a path from u to v shorter than P. If P' shares at least one intermediate node with P_wv, let z be the first such node; then the part of P' from u to z followed by the part of P_wv from z to v is a path from u to v shorter than P. Similarly, P_wv must be a shortest path from w to v. Only question is how to characterize subproblems? Natural possibilities include limiting number of edges on path or limiting intermediary vertices allowed on path. We choose to limit intermediary vertices since characterization of optimal substructure is based on considering intermediary vertices. [Rest to be covered in tutorial] - Step 1: Define an array. - Step 2: Write a recurrence. - Step 3: Compute values bottom-up. - Step 4: Find optimal answer.