6.3

Yuckdonald's Restaurant Placement

Pattern · constrained prefix 1-D table O(n²) 6difficulty
Question — exam style

Yuckdonald's is considering opening a series of restaurants along Quaint Valley Highway (QVH). The n possible locations are along a straight line, and the distances of these locations from the start of QVH are, in miles and in increasing order, m[1], m[2], …, m[n]. The constraints are:

• At each location, Yuckdonald's may open at most one restaurant. The expected profit from opening a restaurant at location i is p[i], where p[i] > 0.
• Any two restaurants should be at least k miles apart, where k is a positive integer.

Input:Positions m[1] < … < m[n], profits p[1..n], integer k.
Output:The maximum expected total profit subject to the spacing constraint.

Model answer

4-part format
aSubproblem in words
T(i) = the maximum total profit obtainable using only locations 1..i, considering location i (open it or skip it), for 0 ≤ i ≤ n.

For each i, let prev(i) = the largest index j < i with m[i] − m[j] ≥ k (0 if none). This is the "take/skip" shape — a 1-D Knapsack cousin.

bRecurrence math only, with base case + bounds
T(0) = 0 T(i) = max{ T(i-1), (skip location i) p[i] + T(prev(i)) } (open at location i) for 1 ≤ i ≤ n

If we open i, the previous restaurant can be no later than prev(i), so we add the best profit up to there. If we skip i, we inherit T(i-1).

cImplementation analysis
(1) # of subproblems
O(n)
(2) Time to fill table
O(n²) — O(n) to find prev(i)
(3) Where answer is extracted
T(n)
(4) Time to extract
O(1)

Note: computing every prev(i) up front with a scan is O(n²); a two-pointer pass makes it O(n), giving O(n) fill. State whichever your recurrence implies — the exam wants consistency, and O(n²) is safe and correct.

Optional pseudocode not required by Ed #9

A mechanical translation of the recurrence. The exam grades the recurrence, not this.

function Yuckdonalds(m[1..n], p[1..n], k):
    # precompute prev(i) = largest j < i with m[i]-m[j] >= k, else 0
    for i = 1 to n:
        prev[i] = 0
        for j = i-1 down to 1:
            if m[i] - m[j] >= k: prev[i] = j; break
    T[0] = 0
    for i = 1 to n:
        T[i] = max(T[i-1], p[i] + T[prev[i]])
    return T[n]

Try it yourself

worked example + self-test

Recurrence: T(0)=0; T(i)=max{T(i−1), p[i]+T(prev(i))}. Answer T(n).

Worked prev(i) + table shown

positions m = [1, 3, 6, 10, 14, 18], profits p = [5, 6, 5, 11, 4, 9], k = 5

i │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 ─────────┼───┼───┼───┼────┼────┼─── prev(i) │ 0 │ 0 │ 1 │ 2 │ 3 │ 4 skip │ 0 │ 5 │ 6 │ 10 │ 17 │ 17 open │ 5 │ 6 │10 │ 17 │ 14 │ 26 T(i) │ 5 │ 6 │10 │ 17 │ 17 │ 26 open = p[i] + T(prev(i)); e.g. T(4) open = 11 + T(2)=11+6 = 17
Answer: T(6) = 26. Opened locations: 2, 4, 6 (positions 3, 10, 18; profits 6+11+9). Spacing 7 and 8, both ≥ 5. ✓  Why the DP is needed: a naive left-to-right greedy (open, then skip until 5 miles clear) opens miles 1, 6, 14 for only 14 — it grabs the cheap early location 1 and gets blocked out of the profit-11 location at mile 10. The DP's skip-vs-open weighing finds 26.
Self-test work prev(i) + table, then reveal

positions m = [2, 5, 9, 12, 16], profits p = [4, 7, 3, 8, 6], k = 4

Reveal answer
Max profit = 21  (open locations 2, 4, 5 → positions 5, 12, 16; profits 7+8+6).