A DP over crates must report the chosen set, not just the optimum — how do you budget memory for that?
answer
- the value survives, the decision does not
- ask what the walk-back actually reads
- one bit per cell, not one number
- recompute halves to trade time for space
- sometimes keeping the table is the answer
basics
~20 sRolling the table away destroys the record of which choice each cell made, so reconstruction needs something kept: the full table, one decision bit per cell, parent pointers, or a divide-and-conquer scheme that recomputes halves to trade time for space.
solid answer
~50 sCompression is only free when the answer is a number. The moment the product has to list the chosen crates, the walk-back needs to read what each state decided, and those are exactly the rows rolling throws away. Four options, in rising cleverness: keep the full table, which is the right answer whenever it fits; store one decision bit per cell instead of the full value, which is the same cell count at a far smaller constant and often enough; use a divide-and-conquer scheme that finds where the optimal path crosses the table's midpoint and recurses, giving space linear in one dimension for roughly double the time; or cap the inputs so the plain table always fits and defend that as a product constraint. I would price all four against the real input distribution and pick the least clever one that fits, because the reconstruction code is what the team maintains.
go deeper
Recall that rolling a DP table keeps the best value but throws away the record of which choice each state made, so listing the chosen items afterwards is no longer possible from the surviving row.
Explain the cheap middle grounds and their cost: one decision bit per cell instead of the full value, or an explicit predecessor per state, both of which keep the same cell count at a different constant.
Price the options against the real input distribution and pick. Be ready to say why the divide-and-conquer linear-space scheme, at roughly double the time, is or is not worth its maintenance cost here.
Own the tradeoff end to end: which resource is genuinely scarce, whether bounding the input is a cheaper contract change than a cleverer algorithm, and who on the team will maintain and debug whatever you ship.
## Why reconstruction and compression pull against each other A DP fill answers "what is the best achievable value?" Reconstruction answers "which decisions achieved it?" — and that second question is answered by reading the table backwards: at the final cell, compare against the cells the recurrence could have come from, decide which predecessor was used, step there, repeat. Every one of those reads is on a row that a rolled implementation has already discarded. That is the whole tension, and it is a real constraint, not a shortcoming of anyone's implementation: the compressed run genuinely does not contain the information any more. You cannot recover it from the surviving row, because the last row records only what was achievable, never how. ## The options, priced **Keep the full table.** Space equal to the number of states, reconstruction is a straightforward backward walk, and the code stays legible enough to debug by printing. For a picker over a few thousand crates and a bounded capacity, the table is a handful of megabytes and this is simply the correct answer. Refusing it because compression is available is optimizing a resource nobody is short of. **Store one decision bit per cell.** The reconstruction walk usually needs far less than the cell's value; often it only needs "was this crate taken here?" Storing a bit instead of a numeric cell keeps the same cell count but shrinks the constant by roughly an order of magnitude or two. That converts many tables that were several times over budget into ones that fit, while leaving the algorithm and the walk-back essentially unchanged. It does not change the asymptotics, so it buys you one scale step, not the next one. **Divide and conquer over the midpoint.** The classic linear-space reconstruction — Hirschberg's scheme — runs the fill forward over one half and backward over the other, both in rolled form, determines where the optimal path crosses the middle, and recurses on the two resulting subproblems. Space drops to linear in one dimension; the recomputation adds up to a geometric series, so the total work is roughly twice the plain fill. It is the genuinely clever option, and it is also the one a future maintainer is least likely to understand, which is a cost that belongs in the decision. **Bound the input.** If the product can say "a pick list covers at most N crates", the table is bounded by construction and none of the above is needed. This is often the cheapest fix available and the one engineers reach for last, because it lives in the product conversation rather than in the code. **Re-solving repeatedly** deserves a mention only to be rejected: pinning one decision and re-running the DP for the rest recovers the choices without extra storage, but at a factor of n more work. It is the option that sounds free and is not. | Option | Extra space | Extra time | Maintenance cost | |---|---|---|---| | Full table | states × cell width | none | lowest | | Decision bit per cell | states × 1 bit | none | low | | Divide and conquer | linear in one dimension | about 2× | high | | Repeated re-solving | none | about n× | medium | | Bounded input | none | none | product change | ## Making the call The decision is not "which is most efficient" but "which resource is actually scarce, and at what input size". Price the table at the real p99 input, not the theoretical maximum — a picker whose realistic worst case is a few thousand crates has no memory problem to solve. If the table genuinely does not fit, try the decision-bit form first, because it preserves the shape of the code. Reach for divide-and-conquer only when the constant-factor win is not enough and the input cannot be bounded, and when you reach for it, pay the associated costs deliberately: a written explanation of why it is there, a reference implementation to test against, and an owner who understands it. The argument to a reviewer pushing on the memory line is made with numbers, not principle: here is the table size at the real input distribution, here is the added running time and the code complexity of the alternative, and here is the fact that listing the chosen crates *is* the feature — the memory exists to deliver it. Equally, if the numbers say the table is 40 GB, no amount of "but the feature needs it" makes the plain table shippable, and the conversation moves to bounding the input. ## What a weak answer sounds like "Roll it, then reconstruct from the last row" — the information is not there. "Parent pointers are basically free" — a pointer per state is usually larger than the value it accompanies. "Always use the linear-space scheme, it is strictly better" — it is strictly better on one axis and worse on two others.
- Decision bits still cost one entry per state — when is that enough?When the constant was what broke you. A bit per cell is roughly one to two orders of magnitude smaller than a numeric cell, so a table that was several times over budget now fits comfortably while the fill and the walk-back keep their shape. It does not touch the asymptotics, so it buys exactly one scale step; if the input can grow tenfold, you will be back with the same conversation.
- Why can't you just re-run the fill and read the choices off as you go?Because the optimal decision at each stage depends on values from states the compressed run discarded — the surviving row records what was achievable, never how. You can pin one decision and re-solve the remaining subproblem to recover the choices one at a time, but that is a linear number of re-solves and roughly n times the work. The divide-and-conquer scheme is the version of that idea that stays affordable.
- How do you defend keeping the full table to a reviewer chasing the memory line?With numbers. State the table size at the real p99 input rather than the theoretical maximum, state the added running time and the code complexity of each alternative, and state that listing the chosen crates is the feature the memory is buying. If the measured table is a few megabytes there is nothing to optimize; if it is tens of gigabytes, the honest answer is that the conversation moves to bounding the input.
saying these in an interview costs you the question
- Says you can always compress and still list the choices
- Claims the answer is recoverable from the surviving row
- Treats parent pointers as free storage
- Assumes reconstruction always needs the full-width table
- Optimizes memory the product never runs short of