Hard Problemsminimizationinterval

Egg Dropping Problem

Given k identical eggs and a building with n floors, where an egg dropped from at or above some unknown critical floor breaks and one dropped below it survives, find the minimum number of trial drops needed, in the worst case, to pin down that critical floor exactly.

Do this lesson first: coin change

Example input

3 eggs, 6 floors

Expected output

3

Break it down

Answer each question out loud before you open it. Getting it wrong here is the useful part. A revealed answer you never guessed at teaches you nothing.

Fill the table

The table pauses before each cell you have to supply. Type the value the recurrence gives, and the write animation confirms it.

Step not started

Press start. The animation stops at every cell YOUR recurrence must fill.

dp[e][0] = 0 down column 0: with no floors there is nothing to find. dp[1][f] = f across the 1-egg row: with a single egg you cannot risk a break that leaves a floor untested, so you drop from floor 1, then 2, and on up, and in the worst case every one of the f floors gets a drop. Every other cell reads 2f cells: the row above at columns 0 to f - 1, one per floor k for the break, and its own row at columns f - 1 down to 0, one per floor k for the survival. All of them sit in the row above or to the left in the same row, so a row-major sweep, left to right along each row, has every read final before the cell needs it. As k rises the break side, dp[e - 1][k - 1], can only grow and the survive side, dp[e][f - k], can only shrink, which is why the best k sits where the two cross.

Rows ↓
Cols →
0
1
2
3
4
5
6
1 egg
0
1
2
3
4
5
6
2 eggs
0
1
2
2
3
3
3
3 eggs
0
1
2
2
3
3
3

Rows ↓row e - 1: how many eggs you hold, so the row labelled 2 eggs is dp[2][...]

Cols →column f: how many floors are left to search, so the cell dp[e][f] is the fewest drops that always find the critical floor

egg-dropping.ts
  1. 1function eggDrop(eggs, floors) {
  2. 2 const dp = Array.from({ length: eggs + 1 }, () => new Array(floors + 1).fill(0));
  3. 3 for (let f = 1; f <= floors; f++) dp[1][f] = f;
  4. 4 for (let e = 2; e <= eggs; e++) {
  5. 5 for (let f = 1; f <= floors; f++) {
  6. 6 let best = Infinity;
  7. 7 for (let k = 1; k <= f; k++) {
  8. 8 const worse = Math.max(dp[e - 1][k - 1], dp[e][f - k]);
  9. 9 best = Math.min(best, 1 + worse);
  10. 10 }
  11. 11 dp[e][f] = best;
  12. 12 }
  13. 13 }
  14. 14 return dp[eggs][floors];
  15. 15}
Base caseComputedBeing readAnswer

The code, the trap, the variations

egg-dropping.ts
  1. 1function eggDrop(eggs, floors) {
  2. 2 const dp = Array.from({ length: eggs + 1 }, () => new Array(floors + 1).fill(0));
  3. 3 for (let f = 1; f <= floors; f++) dp[1][f] = f;
  4. 4 for (let e = 2; e <= eggs; e++) {
  5. 5 for (let f = 1; f <= floors; f++) {
  6. 6 let best = Infinity;
  7. 7 for (let k = 1; k <= f; k++) {
  8. 8 const worse = Math.max(dp[e - 1][k - 1], dp[e][f - k]);
  9. 9 best = Math.min(best, 1 + worse);
  10. 10 }
  11. 11 dp[e][f] = best;
  12. 12 }
  13. 13 }
  14. 14 return dp[eggs][floors];
  15. 15}

Where people go wrong

Scoring a drop by its BETTER outcome: 1 + min(dp[e - 1][k - 1], dp[e][f - k]) where the max belongs. It reads naturally, min everywhere for a minimisation problem, and it is badly wrong: a first drop from floor 1 always has a break side of dp[e - 1][0] = 0, so every computed cell comes out 1 and the answer reads 1 at every slider size, as if one drop could settle any building. The worst case is the whole point of the question, so the inner operation has to be the max. The other classic slip is reaching for binary search: drop from the lower middle floor, floor f / 2 rounded up, while you have more than one egg, then go floor by floor. On the 2-egg row it is right up to 8 floors and then wrong at 9, 10 and 11, needing 5, 5 and 6 drops where the table finds 4, 4 and 5, because halving spends an egg too early and leaves one egg facing a long climb. Round to the upper middle floor instead and the 2-egg row already goes wrong at 6 floors, 4 drops against 3. On the 3-egg row, the one the answer comes from, it happens to match at every slider size, which is exactly how it survives a few hand tests.

  • Flip the question: with t drops and e eggs, how many floors can you settle?

    The state becomes (drops, eggs), and the min and the max both disappear: one drop either breaks, leaving t - 1 drops and e - 1 eggs for the floors below, or survives, leaving t - 1 drops and e eggs for the floors above, so c[t][e] = c[t - 1][e - 1] + c[t - 1][e] + 1. The answer is the smallest t with c[t][e] at least f. With 3 eggs that coverage runs 1, 3, 7, 14 for t = 1 to 4, so 6 floors need 3 drops and 11 floors need 4, the same answers as the table, in O(e × answer) time instead of O(e f²).

  • Find the best first-drop floor without trying every one.

    The recurrence does not change; the scan over k does. The break side dp[e - 1][k - 1] never falls as k rises and the survive side dp[e][f - k] never rises, so the larger of the two can only fall and then only climb, and the best k is where the two sides cross. Binary search for that crossing takes O(log f) per cell instead of O(f), so the table costs O(e f log f).

  • Fix the eggs at 2.

    Only the first two rows remain, and the 2-egg row has a closed form: with t drops, two eggs settle t(t + 1) / 2 floors (drop from floor t, then t + (t - 1), and so on, one floor less each time), so the answer is the smallest t with t(t + 1) / 2 at least f. On this page the 2-egg row reads 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5 from 1 to 11 floors, which is exactly that t. The classic 100 floors need 14.

  • Return the drop plan, not just the count.

    The table is unchanged; remember the winning k at every cell. The plan is a tree, not a list: drop from the winning floor of dp[e][f], then follow dp[e - 1][k - 1] if the egg broke or dp[e][f - k] if it survived, shifting that cell's floors up by k. At 3 eggs and 6 floors the first drop is floor 3, and floor 4 ties with it.