12
CS Homework Solutions Spring . Describe and analyze an efficient algorithm to compute the longest increasing digital subsequence of D. Solution (O(n 4 ) time): First, for any index i and non-negative integer k, let N (i , k ) denote the numerical value of the subarray D[i - k + 1 .. i ] containing the k consecutive input digits ending at D[i ]: N (i , k) := k-1 X j =0 D[i - j ] · 10 j We define a pair of mutually recursive functions: Lmax(i , k ) is the length of the longest increasing digital subsequence of D[1 .. i ] where each number has at most k digits. There are no 0-digit numbers, so Lmax(i ,0)= 0. [If you think the empty string is a valid representation for 0, then you should set Lmax(i ,0)= 1.] If i < k, then no digital subsequence of D[1 .. i ] contains a k-digit number, so Lmax(i , k)= Lmax(i , k - 1). Otherwise, either the longest increasing digital subsequence of D[1 .. i ] with numbers of at most k digits either (a) contains no k-digit numbers, (b) does not use digit D[i ], or (c) ends with the k-digit number D[i - k + 1 .. i ]. Lend(i , k ) is the length of the longest increasing digital subsequence of D[1 .. i ] whose last element (if any) is the k-digit number D[i - k + 1 .. i ]. There are no 0-digit numbers, so Lend(i ,0)= 0. [If you think the empty string is a valid representation for 0, then you should set Lend(i ,0)= 1.] If i < k, no digital subsequence of D[1 .. i ] ends with a k-digit number, so Lend(i , k)= 0. Otherwise, either the longest increasing digital subsequence of D[1 .. i ] whose last element is D[i - k + 1 .. i ] contains no other k-digit numbers, or its second-to-last element is another k-digit number in D[1 .. i - k] whose numerical value is smaller than N (i , k). We need to compute Lmax(n, n). The observations above imply the following mutual recurrences: Lend(i , k)= 0 if k = 0 0 if i < k 1 + max Lmax(i - k, k - 1) max Lend( j , k) j i - k and N ( j , k) < N (i , k) otherwise Lmax(i , k)= 0 if k = 0 Lmax(i , k - 1) if i < k max Lmax(i , k - 1), Lmax(i - 1, k), Lend(i , k) otherwise

CS473 Homework2Solutions Spring2020€¦ · CS473 Homework2Solutions Spring2020 1. Describeandanalyzeanefficientalgorithmtocomputethelongestincreasingdigital subsequenceofD. Solution(O(n4)

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

Page 1: CS473 Homework2Solutions Spring2020€¦ · CS473 Homework2Solutions Spring2020 1. Describeandanalyzeanefficientalgorithmtocomputethelongestincreasingdigital subsequenceofD. Solution(O(n4)

CS 473 Homework 2 Solutions Spring 2020

1. Describe and analyze an efficient algorithm to compute the longest increasing digitalsubsequence of D.

Solution (O(n4) time): First, for any index i and non-negative integer k, let N(i, k)denote the numerical value of the subarray D[i−k+1 .. i] containing the k consecutiveinput digits ending at D[i]:

N(i, k) :=k−1∑

j=0

D[i − j] · 10 j

We define a pair of mutually recursive functions:

• Lmax(i, k) is the length of the longest increasing digital subsequence of D[1 .. i]where each number has at most k digits.

– There are no 0-digit numbers, so Lmax(i, 0) = 0. [If you think the emptystring is a valid representation for 0, then you should set Lmax(i, 0) = 1.]

– If i < k, then no digital subsequence of D[1 .. i] contains a k-digit number,so Lmax(i, k) = Lmax(i, k− 1).

– Otherwise, either the longest increasing digital subsequence of D[1 .. i] withnumbers of at most k digits either (a) contains no k-digit numbers, (b) doesnot use digit D[i], or (c) ends with the k-digit number D[i − k+ 1 .. i].

• Lend(i, k) is the length of the longest increasing digital subsequence of D[1 .. i]whose last element (if any) is the k-digit number D[i − k+ 1 .. i].

– There are no 0-digit numbers, so Lend(i, 0) = 0. [If you think the emptystring is a valid representation for 0, then you should set Lend(i, 0) = 1.]

– If i < k, no digital subsequence of D[1 .. i] ends with a k-digit number, soLend(i, k) = 0.

– Otherwise, either the longest increasing digital subsequence of D[1 .. i]whose last element is D[i − k+ 1 .. i] contains no other k-digit numbers, orits second-to-last element is another k-digit number in D[1 .. i − k] whosenumerical value is smaller than N(i, k).

We need to compute Lmax(n,n). The observations above imply the following mutualrecurrences:

Lend(i, k) =

0 if k = 0

0 if i < k

1+max

Lmax(i − k, k− 1)

max

Lend( j, k)

j ≤ i − k andN( j, k)< N(i, k)

otherwise

Lmax(i, k) =

0 if k = 0

Lmax(i, k− 1) if i < k

max�

Lmax(i, k− 1), Lmax(i − 1, k), Lend(i, k)

otherwise

1

Page 2: CS473 Homework2Solutions Spring2020€¦ · CS473 Homework2Solutions Spring2020 1. Describeandanalyzeanefficientalgorithmtocomputethelongestincreasingdigital subsequenceofD. Solution(O(n4)

CS 473 Homework 2 Solutions Spring 2020

Both arguments of Lmax and Lend are integers between 0 and n, so we canmemoize them into two-dimensional arrays Lend[0 .. n, 0 .. n] and Lmax[0 .. n, 0 .. n].

Each subproblem Lmax(i, k) depends on three recursive subproblems and Lend(i, k).Each subproblem Lend(i, k) depends on i+1= O(n) other subproblems, either earlierin the same row (Lend[ j, k] for some j < i) or in the same roe and previous column(Lend[i, k−1]). Thus, we can fill thememoization tables in row-major order, computingeach value Lend[i, k] just before Lmax[i, k].

LIDSLength(D[1 .. n]):for i← 0 to n

Lend[i, 0]← 0Lmax[i, 0]← 0

for k← 1 to iLend[i, k]← 1+ Lmax[i − k, k− 1]for j← 1 to i − k⟨⟨Compute the sign of N(i, k)− N( j, k)⟩⟩sign← 0for `← k− 1 downto 0

if sign= 0 and D[i − `]> D[ j − `]sign← +1

else if sign= 0 and D[i − `]< D[ j − `]sign←−1

if sign= +1 ⟨⟨if N(i, k)> N( j, k)⟩⟩Lend[i, k]←max {Lend[i, k], 1+ Lend[ j, k]}

Lmax[i, k]←max {Lmax[i, k− 1], Lmax[i − 1, k], Lend[i, k]}

for k← i + 1 to nLend[i, k]← 0Lmax[i, k]← Lmax[i, k− 1]

return Lmax[n, n]

The resulting algorithm runs in O(n4) time. �

Solution (precompute comparisons): The main bottleneck in the previous algo-rithm was the time spent comparing k-digit numbers. We can speed up the algorithmby pre-computing all possible comparisons between numbers of all possible lengths.

To that end, we define a boolean function More(i, j, k) that equals True if andonly if N(i, k) > N( j, k). (More(i, j, k) is well-defined if and only if both k ≤ i andk ≤ j, because that’s when both N(i, k) and N( j, k) are well-defined.) The Morefunction obeys the following recurrence:

More(i, j, k) =

False if k = 0

True if D[i − k+ 1]> D[ j − k+ 1]False if D[i − k+ 1]< D[ j − k+ 1]More(i, j, k− 1) otherwise

We cam memoize this function into a three-dimensional array More[1 .. n, 1 .. n, 0 .. n].

2

Page 3: CS473 Homework2Solutions Spring2020€¦ · CS473 Homework2Solutions Spring2020 1. Describeandanalyzeanefficientalgorithmtocomputethelongestincreasingdigital subsequenceofD. Solution(O(n4)

CS 473 Homework 2 Solutions Spring 2020

Each entry More[i, j, k] depends only on More[i, j, k− 1], so we can fill the array inany order that increases k.

ComputeMore(D[1 .. n])for i← 1 to n

for j← 1 to nMore[i, j, 0]← Falsefor k← 1 to min{i, j}

if D[i − k+ 1]> D[ j − k+ 1]More[i, j, k]← True

else if D[i − k+ 1]< D[ j − k+ 1]More[i, j, k]← False

elseMore[i, j, k]←More[i, j, k− 1]

After we call ComputeMore(D[1 .. n]), any value Lend(i, k) can be computed in O(n)time, using a single loop over possible values of j.

LIDSLength(D[1 .. n]):ComputeMore(D) ⟨⟨ComputeMore[i, j, k] for all i, j, k⟩⟩

for i← 0 to nLend[i, 0]← 0Lmax[i, 0]← 0

for k← 1 to iLend[i, k]← 1+ Lmax[i − k, k− 1]for j← 1 to i − k

if More[i, j, k] ⟨⟨if N(i, k)> N( j, k)⟩⟩Lend[i, k]←max {Lend[i, k], 1+ Lend[ j, k]}

Lmax[i, k]←max {Lmax[i, k− 1], Lmax[i − 1, k], Lend[i, k]}

for k← i + 1 to nLend[i, k]← 0Lmax[i, k]← Lmax[i, k− 1]

return Lmax[n, n]

The resulting algorithm runs in O(n3) time. �

Solution (more careful analysis): The following simple observation lets us improvethe algorithm even more:

There is a longest increasing digital subsequence in whichadjacent numbers differ in length by at most one.

For example, we don’t need to consider any subsequence in which a 5-digit number isfollowed immediately by a 9-digit number; removing the last three (or possibly four)digits of the second number still leaves an increasing digital subsequence.

This observation implies that we never need to consider numbers with more than

3

Page 4: CS473 Homework2Solutions Spring2020€¦ · CS473 Homework2Solutions Spring2020 1. Describeandanalyzeanefficientalgorithmtocomputethelongestincreasingdigital subsequenceofD. Solution(O(n4)

CS 473 Homework 2 Solutions Spring 2020

p2n digits, because

p2n∑

k=1

k =p

2n (p

2n+ 1)2

= n+s

n2> n.

Thus, our algorithm only needs to to consider values of k between 1 andp

2n, both inComputeMore and in the main algorithm, so our running time drops to O(n5/2). �

Solution (data structures FTW): Finally, we can replace the innermost for-loop(with index j) with a single query and a single insertion into a range searchingdata structure, following one of the standard methods for solving the usual longestincreasing subsequence problem in O(n log n) time.

Our data structure stores an integer k and a set S of ordered pairs ( j,`), andsupports the following operations:

• New(k): Create a new data structure with S =∅ and the given value of k.

• Insert(i,`): Insert the pair (i,`) into S.

• Query(i): Among all pairs ( j,`) ∈ S such that N( j, k) < N(i, k), return thelargest value of ` (or −∞ if there are no such pairs).

We implement this data structure as a balanced binary search tree T (for example, ared-black tree, an AVL tree, a scapegoat tree, or a splay tree) with one node per pair( j,`), ordered by the value of N( j, k). For each node v, the corresponding pair ( j,`)is stored in the fields v. j and v.`. In addition, each node v stores the maximum valueof ` among all nodes in the subtree rooted at v, in the field v.max`.

To answer Query(i), we perform a standard search for N(i, k), comparing againstthe value of N( j, k) at each node ( j,`). If N( j, k)< N(i, k), then for every node ( j′,`′)in the left subtree of ( j,`), we also have N( j′, k)< N(i, k). Thus, we can answer thequery by examining O(log n) nodes of the tree. Naïvely, each comparison guiing thesearch requires O(k) time, so the overall query time is O(k logn).

Query(i):v← T.rootmax`←−∞while v 6= Null

if N(v. j, k)< N(i, k) ⟨⟨This takesO(k) time⟩⟩max`←max{max`, v.`}if v.left 6= Null

max`←max{max`, v.max`}v← v.right

elsev← v.left

return max`

To insert a new pair (i,`), we follow the standard algorithm for inserting into thebalanced binary search tree, but modified the insertion algorithm to maintain thefields v.max`. The precise details depend on the choice of balanced binary search

4

Page 5: CS473 Homework2Solutions Spring2020€¦ · CS473 Homework2Solutions Spring2020 1. Describeandanalyzeanefficientalgorithmtocomputethelongestincreasingdigital subsequenceofD. Solution(O(n4)

CS 473 Homework 2 Solutions Spring 2020

tree, but for most standard choices, it suffices to observe that after each rotation, atmost two nodes have incorrect max` fields, and these can be updated in O(1) timewith the line

v.max`←max {v.`, v.left.max`, v.right.max`}

After the new node is inserted, we also update the max` fields along the path from thenew node to the root. The insertion algorithm requires O(log n) comparisons of theform N(v. j, k) < N(i, k), each of which naïvely takes O(k) time, plus O(log n) timefor rotations, max` updates, and other bookkeeping. Thus, altogether, each insertiontakes O(k logn) time.

Finally, to simplify the algorithm slightly, I’ll reverse the order of the for loops,with k in the outer loop and i in the inner loop. Just before the call to Query, thedata structure contains all pairs ( j,Lend[ j, k]) such that 1≤ j ≤ i − k.

LIDSLength(D[1 .. n]):for i← 0 to n

Lend[i, 0]← 0Lmax[i, 0]← 0

for k← 1 to 2p

nNew(k)for i← 1 to n− k

if i > kInsert(i − k,Lend[i − k, k])

Lend[i, k]← 1+max {Lmax[i − k, k− 1], Query(i)}Lmax[i, k]←max {Lmax[i, k− 1], Lmax[i − 1, k], Lend[i, k]}

for i← n− k+ 1 to nLend[i, k]← 0Lmax[i, k]← Lmax[i, k− 1]

return Lmax[n, n]

Each iteration of the outer loop calls Query and Insert O(n) times, and thereforeruns in O(kn log n) time. Thus, the overall algorithm runs in O(n2 log n) time. Thisis the fastest algorithm I’m aware of.

Let me emphasize that this algorithm executes every comparison N( j, k)< N(i, k)naïvely, in O(k) time; ComputeMore requires O(n5/2) time, which is too slow. �

Solution (recursion FTL): We can improve the preprocessing time for comparisonsto O(n2 log n) by using a different recurrence. For any integer i, j, d, let SgnDif(i, j, d)denote the sign of N(i, 2d)− N( j, 2d). This function obeys the following recurrence:

SgnDiff(i, j, 2d) =

sgn(D[i]− D[ j]) if d = 0

SgnDif(i, j, 2d−1) if SgnDif(i, j, 2d−1) 6= 0

SgnDif(i + 2d−1, j + 2d−1, 2d−1) otherwise

We can evaluate this recurrence for all relevant i, j, and d in O(n2 logn) time.

5

Page 6: CS473 Homework2Solutions Spring2020€¦ · CS473 Homework2Solutions Spring2020 1. Describeandanalyzeanefficientalgorithmtocomputethelongestincreasingdigital subsequenceofD. Solution(O(n4)

CS 473 Homework 2 Solutions Spring 2020

After this new preprocessing phase, we can compare two values N(i, k) and N( j, k)in O(log n) time, by breaking both blocks of k digits into O(log n) smaller blocks,each with length a power of 2, following the binary representation of k. With thismodification, our overall algorithm runs in O(n2 log2 n) time. Sad trombone. �

Rubric: 10 points; standard dynamic programming rubric. The target running time isO(n4). Eachfactor of n improvement is worth 10 additional points; for example, a correct algorithm that runsinO(n2.5) time is worth 25 points. All points above 10 are recorded as extra credit. Max 5 pointsfor any slower polynomial-time algorithm.

If a solution describes several improvements to a basic solution, use the correct improvementsas a baseline, but penalize for incorrect running time. These are not the only correct recurrences,but there are lots of ways to screw up (some of which appeared in my solutions the last time Iassigned this problem).

6

Page 7: CS473 Homework2Solutions Spring2020€¦ · CS473 Homework2Solutions Spring2020 1. Describeandanalyzeanefficientalgorithmtocomputethelongestincreasingdigital subsequenceofD. Solution(O(n4)

CS 473 Homework 2 Solutions Spring 2020

2. Describe and analyze an algorithm to determine who wins the game between the Doctorand River, assuming both players play perfectly.

Solution: We start by topologically sorting the input dag G, because that’s alwaysthe first thing one does with a dag. Topological sort labels the vertices with integersfrom 1 to V , so that every edge points from a lower label to a higher label. Because sis the only source, its label is 1, and because t is the only sink, its label is V .

We represent the two players with booleans: True means the Doctor, and Falsemeans River. For any vertices d and r and any boolean who, let WhoWins(d, r,who)denote the winning player when the Doctor’s token starts on d, River’s token tartson r, player who moves first, and both players play perfectly. We need to computeWhoWins(s, t,True).

If the game is not over, then the Doctor wins moving first if and only if at least onemove by the Doctor leads to a position where the Doctor wins moving second, and theDoctor wins moving second if and only if every move by River leads to a position wherethe Doctor wins moving first. (This is the recursive definition of “play perfectly”, forany finite two-player game that cannot end in a draw.) Thus, the function WhoWinscan be computed by the following recursive algorithm:

WhoWins(d, r,who):if d = r

return Trueelse if d = t or r = s

return Falseelse if who= True

return∨

d�vWhoWins(v, r,False)

else if who= Falsereturn

v�rWhoWins(d, v,True)

Thanks to our initial topological sort, we can memoize this function into a V ×V ×2array, indexed by the variables d, r, and who in that order. We can fill the arraywith two nested for loops, decreasing d in one loop and increasing r in the other,considering both players inside the inner loop. The nesting order of the two for-loopsdoesn’t matter. Explicit pseudocode appears on the next page.

For any node v in G, let indeg(v) denote the number of edges entering v (thein-degree of v), and let and outdeg(v) denote the number of edges leaving v (theout-degree of v). For almost every pair of vertices d and r, our algorithm considersall outdeg(d) possible moves for the Doctor and then all indeg(r) possible moves forRiver. Thus, the total running time of our algorithm is at most

V∑

d=1

V∑

r=1

O (outdeg(d) + indeg(r)) .

7

Page 8: CS473 Homework2Solutions Spring2020€¦ · CS473 Homework2Solutions Spring2020 1. Describeandanalyzeanefficientalgorithmtocomputethelongestincreasingdigital subsequenceofD. Solution(O(n4)

CS 473 Homework 2 Solutions Spring 2020

Ignoring the big-Oh constant, we can evaluate this sum in two pieces:

d,r

outdeg(d) =∑

r

d

outdeg(d)

=∑

r

E = V E

d,r

indeg(r) =∑

d

r

indeg(r)

=∑

d

E = V E

Less formally, our algorithm considers all V E pairs (Doctor’s position, River’s move)and all V E pairs (Doctor’s move, River’s position), spending O(1) time on each pair.We conclude that our algorithm runs in O(VE) time.

WhoWins(V, E):label vertices of G in topological orderfor d ← V down to 1

for r ← 1 to Vif d = r

WhoWins[d, r,True]← TrueWhoWins[d, r,False]← True

else if d = t or r = sWhoWins[d, r,True]← FalseWhoWins[d, r,False]← False

elsedoctor← Falsefor all edges d�v

doctor← doctor∨WhoWins[v, r,False]WhoWins[d, r,True]← doctorriver← Falsefor all edges v�r

river← river∧WhoWins[d, v,True]WhoWins[d, r,False]← river

return WhoWins[s, t,True]

Rubric: 10 points, standard dynamic programming rubric. This solution is more detailed thannecessary for full credit. −1 for looser time bounds likeO(V 3) orO(V 2D)where D is maximumdegree.

This is not the only correct description of this algorithm. For example, we can simplify therecurrence slightly by asking whether the first player wins, and then Nanding the results ofrecursive calls, instead of alternating between Ands and Ors. More significantly, instead oftopologically sorting the dag at the beginning, we can discover the correct traversal orders on thefly via depth-first search. Thus, we can rewrite the first three lines ofWHOWINS as follows:

WhoWins(V, E):for all vertices d in postorder ⟨⟨via DFS(G, s) ⟩⟩

for all vertices r in reverse postorder ⟨⟨via DFS(rev(G), t) ⟩⟩⟨⟨and so on⟩⟩

8

Page 9: CS473 Homework2Solutions Spring2020€¦ · CS473 Homework2Solutions Spring2020 1. Describeandanalyzeanefficientalgorithmtocomputethelongestincreasingdigital subsequenceofD. Solution(O(n4)

CS 473 Homework 2 Solutions Spring 2020

No penalty for implicitly assuming that the input graph has more than 10100 vertices; we cansolve the small cases by brute force inO(1) time. In particular, no penalty for implicitly assumingthat the input graph has more than two vertices. (If the graph has only one or two vertices, thenboth players win!) Finally, no penalty for assuming without proof that V = O(E); because G hasonly one source, G must be connected, and therefore E ≥ V − 1.

Solution (configuration graph search): First we construct the configuration graphH = (V ′, E′), which contains a vertex for every possible game configuration and adirected edge for every legal move. Specifically:

• V ′ = V × V × {0,1}. Each vertex (d, r,who) represents the configuration wherethe Doctor’s token is at node d, River’s token is at node r, and it is the Doctor’sturn if and only if who= 1. The number of vertices in H is 2V 2.

• E′ =�

(d, r, 1)�(d ′, r, 0)�

� d�d ′ ∈ E

∪�

(d, r, 0)�(d, r ′, 1)�

� r ′�r ∈ E

. Eachedge represents a legal move by the appropriate player. The number of edgesin H is

r

d outdeg(d) +∑

d

r indeg(r) = 2V E.

If H contains a directed cycle, it must have the form

(d0, r0, 1)�(d1, r0, 0)�(d1, r1, 1)� · · ·�(dk, rk, 1)�(d0, rk, 0)�(d0, r0, 1).

But then the original input dag would contain the cycles d0�d1� · · ·�dk�d0 andr0�rk� · · ·�r1�r0, which is impossible. We conclude that H is also a dag.

For each vertex (d, r,who) let WhoWins(d, r,who) = True if the Doctor winsfrom the configuration (d, r,who) if both players play perfectly; otherwise, letWhoWins(d, r,who) = False. We need to compute WhoWins(s, t, 1). We can evaluatethis function recursively as follows:

WhoWins(d, r,who):if d = r

return Trueelse if d = t or r = s

return Falseelse if who= 1

return∨

(d,r,1)�(d ′,r,0)WhoWins(d ′, r, 0)

else if who= 0

return∧

(d,r,0)�(d,r ′,1)WhoWins(d, r ′, 1)

We can memoize this function into the vertices of H themselves, and we can evaluatethis function at every vertex of H by considering the vertices in reverse topologicalorder (or equivalently, in DFS postorder). The resulting algorithm runs in O(V ′+E′) =O(VE) time. �

Rubric: 10 points = 5 for correctly defining H (= 2 for vertices + 2 for edges + 1 for proving H is adag) + 5 for solving the problem on H (dynamic programming rubric)

9

Page 10: CS473 Homework2Solutions Spring2020€¦ · CS473 Homework2Solutions Spring2020 1. Describeandanalyzeanefficientalgorithmtocomputethelongestincreasingdigital subsequenceofD. Solution(O(n4)

CS 473 Homework 2 Solutions Spring 2020

3. [Extra credit] Describe and analyze an algorithm to compute the minimum total heightrequired to shelve a sequence of n books in O(n logn) time. As in Homework 1 problem 2,the input consists of two arrays H[1 .. n] and W [1 .. n], specifying the height and width ofeach book, and a number L, which is the common length of every shelf.

Solution: As in our earlier solution to HW1.2, we define three functions for all indices iand j:

• MaxH(i, j) =max H[i .. j] is the maximum height among books i through j.

• TotalW(i, j) =∑

W [i .. j] is the total width of books i through j.

• MinTotalH(i) is the minimum total height required to shelve books i through n.

The main function MinTotalH satisfies the recurrence

MinTotalH(i) =

0 if i > n

min

MaxH(i, j) +

MinTotalH( j + 1)

i ≤ j ≤ n andTotalW(i, j)≤ L

otherwise

The main bottleneck in our earlier algorithm is looping over all indices j such thati ≤ j ≤ n and TotalW(i, j)≤ L. To speed up our algorithm, we will replace this loopwith faster data structure operations.

First let’s get rid of TotalW. For any index i, let Last(i) denote the largest index jsuch that books i through j fit on a single shelf. We can compute Last(i) for all i inO(n) time as follows.

ComputeLast(W [1 .. n]):totalW← 0i← 1; j← 1while i ≤ n

if j ≤ n and totalW+W [ j]≤ LtotalW← totalW+W [ j] ⟨⟨Put book j on the shelf ⟩⟩j← j + 1

else ⟨⟨Book j doesn’t exist or doesn’t fit⟩⟩Last[i]← j − 1totalW← totalW−W [i] ⟨⟨Take book i o� the shelf ⟩⟩i← i + 1

return Last[1 .. n]

(After every iteration of the while loop, totalW is the total width of books i throughj − 1, and totalW≤ L.) Now we can simplify our recurrence as follows:

MinTotalH(i) =

0 if i > n

min

MaxH(i, j) +

MinTotalH( j + 1)

i ≤ j ≤ Last[i]�

otherwise

Getting rid of MaxH is a bit more subtle. Intuitively, we’d like to maintain allindices j between i and Last(i) in a priority queue, where the priority of index j isMaxH(i, j)+MinTotalH( j+1). Unfortunately, decrementing i can increase MaxH(i, j)

10

Page 11: CS473 Homework2Solutions Spring2020€¦ · CS473 Homework2Solutions Spring2020 1. Describeandanalyzeanefficientalgorithmtocomputethelongestincreasingdigital subsequenceofD. Solution(O(n4)

CS 473 Homework 2 Solutions Spring 2020

for unbounded many indices j, which means we can’t bound the number of priorityqueue operations. A naive implementation of this approach actually runs in O(n2 log n)time when L is large and the books are sorted from tallest to shortest.

But notice that MinTotalH( j + 1) is a non-increasing function of j—shelving fewerbooks cannot require more vertical space. This observation implies that we only everneed to consider the largest index j such that MaxH(i, j) has any particular value.

To that end, define an index j to be critical for i if either (1) j = Last(i) + 1, or(2) book j is taller than every book from i through j − 1. If j is a critical index, thenMaxH(i, j − 1) is either the height of the previous critical book, or (if j is the smallestcritical index) the height of book i. Moreover, the critical books are sorted by height,except possibly the last one. See the example below.

L

Our algorithm will maintain the set of all critical indices j in two data structures:

• A double-ended queue of critical indices in increasing order. If we implementthis deque as a doubly-linked list, each insertion or deletion (from either end)takes O(1) time.

• A priority queue, where the priority of index j is MaxH(i, j − 1) +MinTotalH( j).If we implement this priority queue as a standard binary heap, each insertionand deletion takes O(log n) time, and we can return the minimum priority inO(1) time.

Now consider how the set of critical indices and their priorities change when wedecrement i.

• All indices larger than Last(i)+1 are no longer critical, so we delete those criticalindices (in decreasing order) from both the right end of the deque and from thepriority queue.

• Index Last(i) + 1 becomes critical, so (if necessary) we insert it into both theright end of the deque and to the priority queue.

• If H[i]≥ H[i+1], then all indices j such that H[ j]≤ H[i] are no longer critical,so we delete those indices (in increasing order) both from the left end of thedeque and from the priority queue.

• Moreover, if H[i]≥ H[i + 1], then the smallest index j such that H[ j]> H[i] isstill critical, but its value MinH(i, j − 1) changes, so we delete and reinsert thatindex with its new priority. This is the only critical index whose priority changes.

• Finally, if H[i]< H[i + 1], then index i + 1 becomes critical, so we insert it intoboth the left end of the deque and the priority queue.

11

Page 12: CS473 Homework2Solutions Spring2020€¦ · CS473 Homework2Solutions Spring2020 1. Describeandanalyzeanefficientalgorithmtocomputethelongestincreasingdigital subsequenceofD. Solution(O(n4)

CS 473 Homework 2 Solutions Spring 2020

For each index i, we perform O(1) insertions into our data structures. Thus, weperform at most O(n) insertions, and therefore at most O(n) deletions, during theentire algorithm. Each insertion and deletion takes O(log n) time, so the total timespent maintaining the data structures is O(n log n).

Finally, here is our main algorithm, with most of the details of data structuremaintenance elided. The function FindMin returns the minimum priority of anyindex in the priority queue.

Shelve(H[1 .. n], W [1 .. n], L):Last[1 .. n]← ComputeLast(W [1 .. n])MinTotalH[n+ 1]← 0Initialize empty deque and priority queuefor i← n down to 1

Update deque and priority queue for index iMinTotalH[i]← FindMin()

return MinTotalH[1]

The algorithm runs in O(n logn) time. In particular, the running time of the algorithmis dominated by the time to maintain the priority queue.a �

aThe algorithm would be faster in practice if we dealt with the smallest and largest critical indicesseparately, instead of keeping them in the priority queue, but that optimization has no effect on theoverall worst-case running time.

Rubric: Maximum 10 extra-credit points, with partial credit at the grader’s discretion. Creditrequires both a description of the algorithm and justification for its correctness. I am absolutelysure that this isn’t the simplest solution; it’s just the one I thought of first.

12