Arrange Balls with adjacent of different types
Given counts of balls of several distinct types, count the number of distinct arrangements of all the balls in a row such that no two balls standing next to each other share the same type.
Do this lesson first: climbing stairsExample input
2 balls of type A, 1 of type B, 1 of type C
Expected output
6
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.
Press start. The animation stops at every cell YOUR recurrence must fill.
A single ball is one arrangement, ending in its own type, so each one-ball column is 1 in its type's row and 0 in the others. Any cell whose pile has no ball of that row's type is 0 too: nothing can end in a type that is not there, which is why a preset without C still shows a C row of zeros. Every other cell reads a column with one ball fewer, and the all-endings cell reads its own column, so the table fills one pile at a time, smallest piles first, and down each column top to bottom.
Cols →
Rows ↓last ball's type
Cols →pile of balls arranged
- 1
function arrangements(p, q, r) { - 2
// dp[a][b][c] = [ends in A, ends in B, ends in C, all endings] - 3
const dp = []; - 4
for (let a = 0; a <= p; a++) { dp[a] = []; - 5
for (let b = 0; b <= q; b++) { dp[a][b] = []; - 6
for (let c = 0; c <= r; c++) { - 7
const have = [a, b, c], cell = [0, 0, 0, 0]; - 8
for (let t = 0; t < 4; t++) { - 9
if (t < 3 && (have[t] === 0 || a + b + c === 1)) { cell[t] = have[t]; continue; } // bases - 10
const rest = [...have]; if (t < 3) rest[t]--; - 11
const from = t < 3 ? dp[rest[0]][rest[1]][rest[2]] : cell; // the all row reads its own column - 12
cell[t] = [0, 1, 2].reduce((sum, s) => (s === t ? sum : sum + from[s]), 0); - 13
} - 14
dp[a][b][c] = cell; - 15
} - 16
} - 17
} - 18
return dp[p][q][r][3]; - 19
}
The code, the trap, the variations
- 1
function arrangements(p, q, r) { - 2
// dp[a][b][c] = [ends in A, ends in B, ends in C, all endings] - 3
const dp = []; - 4
for (let a = 0; a <= p; a++) { dp[a] = []; - 5
for (let b = 0; b <= q; b++) { dp[a][b] = []; - 6
for (let c = 0; c <= r; c++) { - 7
const have = [a, b, c], cell = [0, 0, 0, 0]; - 8
for (let t = 0; t < 4; t++) { - 9
if (t < 3 && (have[t] === 0 || a + b + c === 1)) { cell[t] = have[t]; continue; } // bases - 10
const rest = [...have]; if (t < 3) rest[t]--; - 11
const from = t < 3 ? dp[rest[0]][rest[1]][rest[2]] : cell; // the all row reads its own column - 12
cell[t] = [0, 1, 2].reduce((sum, s) => (s === t ? sum : sum + from[s]), 0); - 13
} - 14
dp[a][b][c] = cell; - 15
} - 16
} - 17
} - 18
return dp[p][q][r][3]; - 19
}
Where people go wrong
Keeping only the all-endings row. Without knowing how a shorter row ended, a step cannot tell whether the new ball lands next to one of its own type, so the rule never gets applied and the count comes out as every arrangement there is: 12 for AABC, against the true 6. The other common slip is treating balls of one type as different from each other. Swap the two A's in ABAC and you get the same row, but a count over labelled balls adds it twice and answers 12 here as well, for a different reason.
Any number k of types (the page draws three).
One more layer per type, and each type cell adds up the other k - 1 types instead of 2. The columns are still every sub-pile, so the number of piles plus one multiplies by (count + 1) for each new type.
The row is a circle, so the first and last balls are neighbours too.
The state must also remember the FIRST ball's type, one table per first type, and the answer adds only the cells whose last type differs from the first. The recurrence inside each table is unchanged; only the one-ball seeds change, since the table for first type f starts from a single f alone. Rows that are rotations of each other still count separately.
Allow two equal neighbours but never three in a row.
The layer becomes (last type, how many of it end the row, 1 or 2). A ball of the same type moves run 1 to run 2 and is not allowed after run 2; a different type resets to run 1. Columns and order stay the same.