Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Coding Interview Patterns  Module 24 – Dynamic programming: foundations

Pick-or-skip DP with adjacency rules

Choose items in a row or a ring so that no two neighbours are taken, for the largest total: the problem, its brute force and the signal for the pattern.

  • Beginner
  • 30 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25.0.4.1, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 7 of 30 minutes free
Free: Two problems: stalls in a row, lamps in a ring · The brute force, and why it fails · What to notice. With a pass: the other 11 sections and 3 interview answers, about 23 minutes.

What you will learn

  • Choose non-adjacent items in a row for the largest total with a pick-or-skip table
  • Prove that skipping or taking each item covers every allowed choice
  • Solve the ring version, where the first and the last items are neighbours
  • Rebuild the chosen items from the table
  • Solve a variant where taking a value rules out its neighbouring values

Before you start

What you’ll learn · Before you start

What you will learn

  • Choose non-adjacent items in a row for the largest total with a pick-or-skip table
  • Prove that skipping or taking each item covers every allowed choice
  • Solve the ring version, where the first and the last items are neighbours
  • Rebuild the chosen items from the table
  • Solve a variant where taking a value rules out its neighbouring values

Before you start

On this page

Pick-or-skip DP chooses items from a line when taking an item rules out its neighbours: stalls along a lane, houses on a street, shifts on consecutive days. Each item is either taken or skipped, and a choice reaches only one place forward, so a table with one entry per item and two choices per entry finds the largest total in linear time. A ring adds one rule, that the first and the last items are neighbours too. This lesson poses both shapes as original problems, the row and the ring, and shows why trying every set of items cannot work.

Two problems: stalls in a row, lamps in a ring

Problem A, stalls in a row. A market lane has n stalls in a row, and stall i gives a discount of discounts[i]. Two neighbouring stalls share one counter that serves a shopper only once, so you never visit two stalls side by side. Return the largest total discount. Limits: 0 ≤ n ≤ 100,000 and 0 ≤ discounts[i] ≤ 10,000.

  • [6, 2, 3, 9, 4] gives 15, from the stalls worth 6 and 9.
  • [5, 9, 6] gives 11: the 9 is the largest, but taking it rules out both 5 and 6.
  • [8, 1, 1, 8] gives 16, from the two ends.

Problem B, lamps in a ring. Lamp posts stand around a lake, and lamp i lights values[i] metres of the path. Neighbouring lamps share a fuse that cannot carry both, and the last lamp is the neighbour of the first. Same limits.

  • [4, 1, 2, 7, 5, 3, 1] gives 14, from the lamps worth 4, 7 and 3.
  • [9, 2, 9] gives 9: in a row the two 9s would give 18, but in the ring they are neighbours.
Five stalls in a row, best two marked, and seven lamps in a circle with a dashed link from the last to the first, best three marked.Stalls in a row: best 15623946 + 9 = 15; the two ends may both be chosenLamps in a ring: best 144127531last and first:neighbours4 + 7 + 3 = 14; the dashed link joins thelast lamp to the first, so not both

A row and a ring, with the best choices marked

Text description of the diagram

The upper part shows five stalls in a row with discounts 6, 2, 3, 9 and 4, each joined by a line to the stall beside it. The stalls worth 6 and 9 have a double border: they are not neighbours, and together they give the best total, 15. In a row the two end stalls are not neighbours, so both could be chosen.

The lower part shows seven lamps in a circle with values 4, 1, 2, 7, 5, 3 and 1, going clockwise from the top. Each lamp is joined to the next, and a dashed link, labelled "last and first: neighbours", joins the last lamp to the first. The lamps worth 4, 7 and 3 have a double border: no two of them are joined, and together they give the best total, 14.

The dashed link is the only difference between the two shapes: in the ring, the first and the last lamp can never both be lit.

The brute force, and why it fails

Try every set of stalls, drop the sets with two neighbours, and keep the best total:

Every set of stalls, and how many are allowed Python · brute/stalls_brute.py
import math
from itertools import product


def best_visit_brute(discounts):
    """Tries every set of stalls, keeps those with no two neighbours, and returns the best total and how many sets it tried."""
    best, best_stalls, tried = 0, [], 0
    for visit in product((False, True), repeat=len(discounts)):
        tried += 1
        if any(visit[i] and visit[i + 1] for i in range(len(visit) - 1)):
            continue  # two neighbouring stalls
        total = sum(d for d, v in zip(discounts, visit) if v)
        if total > best:
            best, best_stalls = total, [i for i, v in enumerate(visit) if v]
    return best, best_stalls, tried


for discounts in ([6, 2, 3, 9, 4], [5, 9, 6], [8, 1, 1, 8], [7]):
    best, stalls, tried = best_visit_brute(discounts)
    print(f"{discounts}: {best}, from stalls {stalls} ({tried} sets tried)")


def allowed_sets(n):
    """How many sets of stalls have no two neighbours, counted without listing them."""
    with_last, without_last = 0, 1  # for a row of 0 stalls: only the empty set
    for _ in range(n):
        with_last, without_last = without_last, with_last + without_last
    return with_last + without_last


for n in (10, 20, 40, 100_000):
    count = allowed_sets(n)
    if n < 1000:
        print(f"{n:,} stalls: {count:,} allowed sets")
    else:
        print(f"{n:,} stalls: a number of allowed sets with {int(math.log10(count)) + 1:,} digits")

Output

[6, 2, 3, 9, 4]: 15, from stalls [0, 3] (32 sets tried)
[5, 9, 6]: 11, from stalls [0, 2] (8 sets tried)
[8, 1, 1, 8]: 16, from stalls [0, 3] (16 sets tried)
[7]: 7, from stalls [0] (2 sets tried)
10 stalls: 144 allowed sets
20 stalls: 17,711 allowed sets
40 stalls: 267,914,296 allowed sets
100,000 stalls: a number of allowed sets with 20,899 digits

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 stalls_brute.py

The answers are right, but 5 stalls already need 32 sets, and the allowed sets alone grow by a factor of about 1.6 per stall: 144 for 10 stalls, 267,914,296 for 40, and for 100,000 stalls a count with 20,899 digits. The counts are Fibonacci numbers, so no listing of them can finish at the stated size.

What to notice

  • Every item has two choices, take or skip, and taking it rules out only the items just before and just after it.
  • The largest item is not always taken, because it can block two neighbours worth more, as in [5, 9, 6].
  • The ring differs from the row in one pair, the first and the last item, so fixing what happens at one end turns it back into a row.

The choice at each stall, and why it is enough Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • A dry run, and two numbers instead of a table
  • The code in five languages
  • Around the lake: the ring as two rows
  • Which stalls? Rebuilding the choice from the table
  • Values that rule out their neighbours: points per value
  • Five bugs and the smallest inputs that break them
  • Exercise
    • Light the lamps around a lake, never two neighbours
  • Check yourself
  • A practice set with sample answers
  • Interview questions
    • Items stand in a row and no two neighbours may both be taken. Write the pick-or-skip recurrence for the largest total, prove it, and show with the smallest input you can find why "take the largest item left and drop its neighbours" fails.
    • Items now stand in a ring, so the first and the last items are neighbours too. How do you reuse a tested solution for a row? Prove your method right, name the ring sizes that need care, and explain how to return the chosen items instead of the total.
    • The stalls now form a tree: a stall's neighbours are its parent and its children, and no two neighbours may both be taken. There are 100,000 stalls, given as a parent array. What does each stall store, in what order do you fill it, and how do you avoid recursion 100,000 levels deep?
  • Key takeaways

7 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 23 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.