Files
codetutor/frontend/src/content/algorithms/coin-change.ts

1112 lines
31 KiB
TypeScript

import type { AlgorithmDefinition } from '@/lib/visualizations/types';
// Helper to create a DP cell
function dpCell(
col: number,
value: number | string,
state: 'normal' | 'highlighted' | 'comparing' | 'computing' | 'success' | 'dimmed' = 'normal'
) {
return {
id: `dp-${col}`,
value,
row: 0,
col,
state,
};
}
// Helper to create coins array element
function coinElement(
index: number,
value: number,
state: 'normal' | 'highlighted' | 'dimmed' | 'success' | 'comparing' = 'normal'
) {
return { value, index, state };
}
export const coinChangeAlgorithm: AlgorithmDefinition = {
id: 'coin-change',
title: 'Coin Change - Dynamic Programming',
slug: 'coin-change',
pattern: {
name: 'Dynamic Programming',
description:
'Break down problems into overlapping subproblems and build solutions from optimal substructure.',
},
problemStatement:
'Given coins of different denominations and a total amount, find the minimum number of coins needed to make that amount. If it cannot be made, return -1.',
intuition:
'For each amount from 1 to target, we compute the minimum coins needed by checking all coin denominations. dp[i] = min coins needed for amount i. For each coin, if coin <= i, we can use it: dp[i] = min(dp[i], dp[i-coin] + 1).',
code: {
language: 'python',
code: `def coin_change(coins: list[int], amount: int) -> int:
# Initialize dp with infinity; dp[0] = 0
dp = [float('inf')] * (amount + 1)
dp[0] = 0
# Build solution for each amount
for i in range(1, amount + 1):
for coin in coins:
if i - coin >= 0:
dp[i] = min(dp[i], dp[i - coin] + 1)
return dp[amount] if dp[amount] != float('inf') else -1`,
},
initialExample: {
input: { coins: [1, 2, 5], amount: 11 },
expected: 3,
},
steps: [
// Phase 1: Problem (2 steps)
{
id: 'problem-1',
phase: 'problem',
explanation:
'Given coins [1, 2, 5] and amount 11, find the minimum number of coins to make exactly 11.',
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'normal'),
coinElement(1, 2, 'normal'),
coinElement(2, 5, 'normal'),
],
},
],
pointers: [],
variables: [
{ id: 'amount', name: 'amount', value: 11 },
],
calculations: [],
},
},
{
id: 'problem-2',
phase: 'problem',
explanation:
'A brute-force approach tries all combinations - exponential time O(S^n). Can we do better by reusing solutions to smaller amounts?',
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'normal'),
coinElement(1, 2, 'normal'),
coinElement(2, 5, 'normal'),
],
},
],
pointers: [],
variables: [
{ id: 'amount', name: 'amount', value: 11 },
],
calculations: [],
},
},
// Phase 2: Intuition (4 steps)
{
id: 'intuition-1',
phase: 'intuition',
explanation:
'Key insight: If we know the minimum coins for amount 10, we can make 11 with one more coin of value 1. This is optimal substructure.',
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'highlighted'),
coinElement(1, 2, 'normal'),
coinElement(2, 5, 'normal'),
],
},
],
pointers: [],
variables: [
{ id: 'amount', name: 'amount', value: 11 },
],
calculations: [
{ id: 'calc-1', expression: 'dp[11] = dp[10] + 1', result: '?', position: 'above' },
],
},
},
{
id: 'intuition-2',
phase: 'intuition',
explanation:
'We can also make 11 from amount 9 (using coin 2) or amount 6 (using coin 5). We take the minimum of all options.',
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'comparing'),
coinElement(1, 2, 'comparing'),
coinElement(2, 5, 'comparing'),
],
},
],
pointers: [],
variables: [
{ id: 'amount', name: 'amount', value: 11 },
],
calculations: [
{ id: 'calc-1', expression: 'min(dp[10]+1, dp[9]+1, dp[6]+1)', result: '?', position: 'above' },
],
},
},
{
id: 'intuition-3',
phase: 'intuition',
explanation:
'Build solutions bottom-up: start from amount 0, compute minimum coins for each amount 1, 2, 3, ... up to target.',
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'normal'),
coinElement(1, 2, 'normal'),
coinElement(2, 5, 'normal'),
],
},
],
pointers: [],
variables: [],
calculations: [],
grids: [
{
id: 'dp',
label: 'DP Table (min coins for each amount)',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, '?'),
dpCell(1, '?'),
dpCell(2, '?'),
dpCell(3, '?'),
dpCell(4, '?'),
dpCell(5, '?'),
dpCell(6, '?'),
dpCell(7, '?'),
dpCell(8, '?'),
dpCell(9, '?'),
dpCell(10, '?'),
dpCell(11, '?'),
]],
},
],
},
},
{
id: 'intuition-4',
phase: 'intuition',
explanation:
'State transition: dp[i] = min(dp[i], dp[i - coin] + 1) for each valid coin. Base case: dp[0] = 0 (zero coins for amount zero).',
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'normal'),
coinElement(1, 2, 'normal'),
coinElement(2, 5, 'normal'),
],
},
],
pointers: [],
variables: [],
calculations: [
{ id: 'calc-1', expression: 'dp[i] = min(dp[i], dp[i-coin] + 1)', result: '', position: 'above' },
],
grids: [
{
id: 'dp',
label: 'DP Table',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, 0, 'success'),
dpCell(1, '?'),
dpCell(2, '?'),
dpCell(3, '?'),
dpCell(4, '?'),
dpCell(5, '?'),
dpCell(6, '?'),
dpCell(7, '?'),
dpCell(8, '?'),
dpCell(9, '?'),
dpCell(10, '?'),
dpCell(11, '?'),
]],
},
],
},
},
// Phase 3: Pattern (2 steps)
{
id: 'pattern-1',
phase: 'pattern',
explanation:
'Dynamic Programming pattern: Define state (dp[i] = min coins for amount i), identify transition (try each coin), set base case (dp[0] = 0).',
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'normal'),
coinElement(1, 2, 'normal'),
coinElement(2, 5, 'normal'),
],
},
],
pointers: [],
variables: [
{ id: 'state', name: 'State', value: 'dp[i] = min coins' },
],
calculations: [],
},
},
{
id: 'pattern-2',
phase: 'pattern',
explanation:
'Time: O(amount * coins), Space: O(amount). Each amount computed once, checking all coins.',
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'normal'),
coinElement(1, 2, 'normal'),
coinElement(2, 5, 'normal'),
],
},
],
pointers: [],
variables: [
{ id: 'time', name: 'Time', value: 'O(amount * coins)' },
{ id: 'space', name: 'Space', value: 'O(amount)' },
],
calculations: [],
},
},
// Phase 4: Code walkthrough (4 steps)
{
id: 'code-1',
phase: 'code',
explanation:
'Initialize dp array with infinity (impossible). dp[0] = 0 because we need 0 coins for amount 0.',
codeLine: 3,
codeHighlightLines: [2, 3, 4],
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'normal'),
coinElement(1, 2, 'normal'),
coinElement(2, 5, 'normal'),
],
},
],
pointers: [],
variables: [],
calculations: [],
grids: [
{
id: 'dp',
label: 'DP Table',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, 0, 'highlighted'),
dpCell(1, '∞'),
dpCell(2, '∞'),
dpCell(3, '∞'),
dpCell(4, '∞'),
dpCell(5, '∞'),
dpCell(6, '∞'),
dpCell(7, '∞'),
dpCell(8, '∞'),
dpCell(9, '∞'),
dpCell(10, '∞'),
dpCell(11, '∞'),
]],
},
],
},
},
{
id: 'code-2',
phase: 'code',
explanation:
'Outer loop: iterate through amounts 1 to target. For each amount, we compute the minimum coins.',
codeLine: 7,
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'normal'),
coinElement(1, 2, 'normal'),
coinElement(2, 5, 'normal'),
],
},
],
pointers: [],
variables: [
{ id: 'i', name: 'i', value: '1 to amount' },
],
calculations: [],
},
},
{
id: 'code-3',
phase: 'code',
explanation:
'Inner loop: try each coin. If coin fits (i - coin >= 0), update dp[i] with minimum.',
codeLine: 8,
codeHighlightLines: [8, 9, 10],
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'comparing'),
coinElement(1, 2, 'comparing'),
coinElement(2, 5, 'comparing'),
],
},
],
pointers: [],
variables: [],
calculations: [
{ id: 'calc-1', expression: 'dp[i] = min(dp[i], dp[i-coin] + 1)', result: '', position: 'above' },
],
},
},
{
id: 'code-4',
phase: 'code',
explanation:
'Return dp[amount] if reachable, else -1. Infinity means the amount cannot be made with given coins.',
codeLine: 12,
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'normal'),
coinElement(1, 2, 'normal'),
coinElement(2, 5, 'normal'),
],
},
],
pointers: [],
variables: [],
calculations: [],
},
},
// Phase 5: Execution (~25 steps)
// dp[0] = 0 (base case)
{
id: 'exec-1',
phase: 'execution',
explanation: 'Initialize: dp[0] = 0 (base case), all others = infinity.',
codeLine: 4,
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'normal'),
coinElement(1, 2, 'normal'),
coinElement(2, 5, 'normal'),
],
},
],
pointers: [],
variables: [
{ id: 'i', name: 'i', value: '-' },
],
calculations: [],
grids: [
{
id: 'dp',
label: 'DP Table',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, 0, 'success'),
dpCell(1, '∞'),
dpCell(2, '∞'),
dpCell(3, '∞'),
dpCell(4, '∞'),
dpCell(5, '∞'),
dpCell(6, '∞'),
dpCell(7, '∞'),
dpCell(8, '∞'),
dpCell(9, '∞'),
dpCell(10, '∞'),
dpCell(11, '∞'),
]],
},
],
},
},
// Compute dp[1]
{
id: 'exec-2',
phase: 'execution',
explanation: 'Compute dp[1]: Try coin 1. dp[1] = min(∞, dp[0] + 1) = min(∞, 1) = 1.',
codeLine: 10,
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'highlighted'),
coinElement(1, 2, 'dimmed'),
coinElement(2, 5, 'dimmed'),
],
},
],
pointers: [],
variables: [
{ id: 'i', name: 'i', value: 1 },
{ id: 'coin', name: 'coin', value: 1 },
],
calculations: [
{ id: 'calc-1', expression: 'min(∞, dp[0]+1)', result: '1', position: 'above' },
],
grids: [
{
id: 'dp',
label: 'DP Table',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, 0, 'comparing'),
dpCell(1, 1, 'computing'),
dpCell(2, '∞'),
dpCell(3, '∞'),
dpCell(4, '∞'),
dpCell(5, '∞'),
dpCell(6, '∞'),
dpCell(7, '∞'),
dpCell(8, '∞'),
dpCell(9, '∞'),
dpCell(10, '∞'),
dpCell(11, '∞'),
]],
},
],
gridPointers: [
{ id: 'ptr-i', name: 'i', row: 0, col: 1, color: 'current' },
{ id: 'ptr-dep', name: 'i-1', row: 0, col: 0, color: 'dependency' },
],
},
},
// Compute dp[2]
{
id: 'exec-3',
phase: 'execution',
explanation: 'Compute dp[2]: Try coin 1 -> dp[1]+1=2. Try coin 2 -> dp[0]+1=1. dp[2] = min(2, 1) = 1.',
codeLine: 10,
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'comparing'),
coinElement(1, 2, 'highlighted'),
coinElement(2, 5, 'dimmed'),
],
},
],
pointers: [],
variables: [
{ id: 'i', name: 'i', value: 2 },
{ id: 'coin', name: 'coin', value: 2 },
],
calculations: [
{ id: 'calc-1', expression: 'min(dp[1]+1, dp[0]+1)', result: '1', position: 'above' },
],
grids: [
{
id: 'dp',
label: 'DP Table',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, 0, 'comparing'),
dpCell(1, 1, 'success'),
dpCell(2, 1, 'computing'),
dpCell(3, '∞'),
dpCell(4, '∞'),
dpCell(5, '∞'),
dpCell(6, '∞'),
dpCell(7, '∞'),
dpCell(8, '∞'),
dpCell(9, '∞'),
dpCell(10, '∞'),
dpCell(11, '∞'),
]],
},
],
gridPointers: [
{ id: 'ptr-i', name: 'i', row: 0, col: 2, color: 'current' },
],
},
},
// Compute dp[3]
{
id: 'exec-4',
phase: 'execution',
explanation: 'Compute dp[3]: coin 1 -> dp[2]+1=2, coin 2 -> dp[1]+1=2. dp[3] = 2.',
codeLine: 10,
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'comparing'),
coinElement(1, 2, 'comparing'),
coinElement(2, 5, 'dimmed'),
],
},
],
pointers: [],
variables: [
{ id: 'i', name: 'i', value: 3 },
],
calculations: [
{ id: 'calc-1', expression: 'min(dp[2]+1, dp[1]+1)', result: '2', position: 'above' },
],
grids: [
{
id: 'dp',
label: 'DP Table',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, 0, 'normal'),
dpCell(1, 1, 'comparing'),
dpCell(2, 1, 'comparing'),
dpCell(3, 2, 'computing'),
dpCell(4, '∞'),
dpCell(5, '∞'),
dpCell(6, '∞'),
dpCell(7, '∞'),
dpCell(8, '∞'),
dpCell(9, '∞'),
dpCell(10, '∞'),
dpCell(11, '∞'),
]],
},
],
gridPointers: [
{ id: 'ptr-i', name: 'i', row: 0, col: 3, color: 'current' },
],
},
},
// Compute dp[4]
{
id: 'exec-5',
phase: 'execution',
explanation: 'Compute dp[4]: coin 1 -> dp[3]+1=3, coin 2 -> dp[2]+1=2. dp[4] = 2.',
codeLine: 10,
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'comparing'),
coinElement(1, 2, 'highlighted'),
coinElement(2, 5, 'dimmed'),
],
},
],
pointers: [],
variables: [
{ id: 'i', name: 'i', value: 4 },
],
calculations: [
{ id: 'calc-1', expression: 'min(dp[3]+1, dp[2]+1)', result: '2', position: 'above' },
],
grids: [
{
id: 'dp',
label: 'DP Table',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, 0, 'normal'),
dpCell(1, 1, 'normal'),
dpCell(2, 1, 'comparing'),
dpCell(3, 2, 'success'),
dpCell(4, 2, 'computing'),
dpCell(5, '∞'),
dpCell(6, '∞'),
dpCell(7, '∞'),
dpCell(8, '∞'),
dpCell(9, '∞'),
dpCell(10, '∞'),
dpCell(11, '∞'),
]],
},
],
gridPointers: [
{ id: 'ptr-i', name: 'i', row: 0, col: 4, color: 'current' },
],
},
},
// Compute dp[5] - First time using coin 5!
{
id: 'exec-6',
phase: 'execution',
explanation: 'Compute dp[5]: coin 1 -> 3, coin 2 -> 3, coin 5 -> dp[0]+1=1. dp[5] = 1!',
codeLine: 10,
decision: {
question: 'Which coin gives minimum?',
answer: 'Coin 5: dp[0]+1 = 1',
action: 'Use one coin of 5',
},
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'comparing'),
coinElement(1, 2, 'comparing'),
coinElement(2, 5, 'highlighted'),
],
},
],
pointers: [],
variables: [
{ id: 'i', name: 'i', value: 5 },
],
calculations: [
{ id: 'calc-1', expression: 'min(3, 3, dp[0]+1)', result: '1', position: 'above' },
],
grids: [
{
id: 'dp',
label: 'DP Table',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, 0, 'comparing'),
dpCell(1, 1, 'normal'),
dpCell(2, 1, 'normal'),
dpCell(3, 2, 'normal'),
dpCell(4, 2, 'success'),
dpCell(5, 1, 'computing'),
dpCell(6, '∞'),
dpCell(7, '∞'),
dpCell(8, '∞'),
dpCell(9, '∞'),
dpCell(10, '∞'),
dpCell(11, '∞'),
]],
},
],
gridPointers: [
{ id: 'ptr-i', name: 'i', row: 0, col: 5, color: 'current' },
{ id: 'ptr-dep', name: 'i-5', row: 0, col: 0, color: 'dependency' },
],
},
},
// Compute dp[6]
{
id: 'exec-7',
phase: 'execution',
explanation: 'Compute dp[6]: coin 1 -> dp[5]+1=2, coin 2 -> dp[4]+1=3, coin 5 -> dp[1]+1=2. dp[6] = 2.',
codeLine: 10,
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'highlighted'),
coinElement(1, 2, 'comparing'),
coinElement(2, 5, 'comparing'),
],
},
],
pointers: [],
variables: [
{ id: 'i', name: 'i', value: 6 },
],
calculations: [
{ id: 'calc-1', expression: 'min(dp[5]+1, dp[4]+1, dp[1]+1)', result: '2', position: 'above' },
],
grids: [
{
id: 'dp',
label: 'DP Table',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, 0, 'normal'),
dpCell(1, 1, 'comparing'),
dpCell(2, 1, 'normal'),
dpCell(3, 2, 'normal'),
dpCell(4, 2, 'comparing'),
dpCell(5, 1, 'comparing'),
dpCell(6, 2, 'computing'),
dpCell(7, '∞'),
dpCell(8, '∞'),
dpCell(9, '∞'),
dpCell(10, '∞'),
dpCell(11, '∞'),
]],
},
],
gridPointers: [
{ id: 'ptr-i', name: 'i', row: 0, col: 6, color: 'current' },
],
},
},
// Compute dp[7]
{
id: 'exec-8',
phase: 'execution',
explanation: 'Compute dp[7]: coin 1 -> 3, coin 2 -> dp[5]+1=2, coin 5 -> dp[2]+1=2. dp[7] = 2.',
codeLine: 10,
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'comparing'),
coinElement(1, 2, 'highlighted'),
coinElement(2, 5, 'highlighted'),
],
},
],
pointers: [],
variables: [
{ id: 'i', name: 'i', value: 7 },
],
calculations: [
{ id: 'calc-1', expression: 'min(3, dp[5]+1, dp[2]+1)', result: '2', position: 'above' },
],
grids: [
{
id: 'dp',
label: 'DP Table',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, 0, 'normal'),
dpCell(1, 1, 'normal'),
dpCell(2, 1, 'comparing'),
dpCell(3, 2, 'normal'),
dpCell(4, 2, 'normal'),
dpCell(5, 1, 'comparing'),
dpCell(6, 2, 'success'),
dpCell(7, 2, 'computing'),
dpCell(8, '∞'),
dpCell(9, '∞'),
dpCell(10, '∞'),
dpCell(11, '∞'),
]],
},
],
gridPointers: [
{ id: 'ptr-i', name: 'i', row: 0, col: 7, color: 'current' },
],
},
},
// Compute dp[8]
{
id: 'exec-9',
phase: 'execution',
explanation: 'Compute dp[8]: coin 1 -> 3, coin 2 -> 3, coin 5 -> dp[3]+1=3. dp[8] = 3.',
codeLine: 10,
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'comparing'),
coinElement(1, 2, 'comparing'),
coinElement(2, 5, 'comparing'),
],
},
],
pointers: [],
variables: [
{ id: 'i', name: 'i', value: 8 },
],
calculations: [
{ id: 'calc-1', expression: 'min(dp[7]+1, dp[6]+1, dp[3]+1)', result: '3', position: 'above' },
],
grids: [
{
id: 'dp',
label: 'DP Table',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, 0, 'normal'),
dpCell(1, 1, 'normal'),
dpCell(2, 1, 'normal'),
dpCell(3, 2, 'comparing'),
dpCell(4, 2, 'normal'),
dpCell(5, 1, 'normal'),
dpCell(6, 2, 'comparing'),
dpCell(7, 2, 'comparing'),
dpCell(8, 3, 'computing'),
dpCell(9, '∞'),
dpCell(10, '∞'),
dpCell(11, '∞'),
]],
},
],
gridPointers: [
{ id: 'ptr-i', name: 'i', row: 0, col: 8, color: 'current' },
],
},
},
// Compute dp[9]
{
id: 'exec-10',
phase: 'execution',
explanation: 'Compute dp[9]: coin 1 -> 4, coin 2 -> dp[7]+1=3, coin 5 -> dp[4]+1=3. dp[9] = 3.',
codeLine: 10,
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'comparing'),
coinElement(1, 2, 'highlighted'),
coinElement(2, 5, 'highlighted'),
],
},
],
pointers: [],
variables: [
{ id: 'i', name: 'i', value: 9 },
],
calculations: [
{ id: 'calc-1', expression: 'min(4, dp[7]+1, dp[4]+1)', result: '3', position: 'above' },
],
grids: [
{
id: 'dp',
label: 'DP Table',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, 0, 'normal'),
dpCell(1, 1, 'normal'),
dpCell(2, 1, 'normal'),
dpCell(3, 2, 'normal'),
dpCell(4, 2, 'comparing'),
dpCell(5, 1, 'normal'),
dpCell(6, 2, 'normal'),
dpCell(7, 2, 'comparing'),
dpCell(8, 3, 'success'),
dpCell(9, 3, 'computing'),
dpCell(10, '∞'),
dpCell(11, '∞'),
]],
},
],
gridPointers: [
{ id: 'ptr-i', name: 'i', row: 0, col: 9, color: 'current' },
],
},
},
// Compute dp[10]
{
id: 'exec-11',
phase: 'execution',
explanation: 'Compute dp[10]: coin 1 -> 4, coin 2 -> 4, coin 5 -> dp[5]+1=2. dp[10] = 2!',
codeLine: 10,
decision: {
question: 'Which coin gives minimum?',
answer: 'Coin 5: dp[5]+1 = 2',
action: 'Two coins of 5',
},
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'comparing'),
coinElement(1, 2, 'comparing'),
coinElement(2, 5, 'highlighted'),
],
},
],
pointers: [],
variables: [
{ id: 'i', name: 'i', value: 10 },
],
calculations: [
{ id: 'calc-1', expression: 'min(4, 4, dp[5]+1)', result: '2', position: 'above' },
],
grids: [
{
id: 'dp',
label: 'DP Table',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, 0, 'normal'),
dpCell(1, 1, 'normal'),
dpCell(2, 1, 'normal'),
dpCell(3, 2, 'normal'),
dpCell(4, 2, 'normal'),
dpCell(5, 1, 'comparing'),
dpCell(6, 2, 'normal'),
dpCell(7, 2, 'normal'),
dpCell(8, 3, 'normal'),
dpCell(9, 3, 'success'),
dpCell(10, 2, 'computing'),
dpCell(11, '∞'),
]],
},
],
gridPointers: [
{ id: 'ptr-i', name: 'i', row: 0, col: 10, color: 'current' },
{ id: 'ptr-dep', name: 'i-5', row: 0, col: 5, color: 'dependency' },
],
},
},
// Compute dp[11] - The answer!
{
id: 'exec-12',
phase: 'execution',
explanation: 'Compute dp[11]: coin 1 -> dp[10]+1=3, coin 2 -> dp[9]+1=4, coin 5 -> dp[6]+1=3. dp[11] = 3.',
codeLine: 10,
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'highlighted'),
coinElement(1, 2, 'comparing'),
coinElement(2, 5, 'highlighted'),
],
},
],
pointers: [],
variables: [
{ id: 'i', name: 'i', value: 11 },
],
calculations: [
{ id: 'calc-1', expression: 'min(dp[10]+1, dp[9]+1, dp[6]+1)', result: '3', position: 'above' },
],
grids: [
{
id: 'dp',
label: 'DP Table',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, 0, 'normal'),
dpCell(1, 1, 'normal'),
dpCell(2, 1, 'normal'),
dpCell(3, 2, 'normal'),
dpCell(4, 2, 'normal'),
dpCell(5, 1, 'normal'),
dpCell(6, 2, 'comparing'),
dpCell(7, 2, 'normal'),
dpCell(8, 3, 'normal'),
dpCell(9, 3, 'comparing'),
dpCell(10, 2, 'comparing'),
dpCell(11, 3, 'computing'),
]],
},
],
gridPointers: [
{ id: 'ptr-i', name: 'i', row: 0, col: 11, color: 'current' },
],
},
},
// Final result
{
id: 'exec-13',
phase: 'execution',
explanation: 'Complete! dp[11] = 3. We need minimum 3 coins: 5 + 5 + 1 = 11.',
codeLine: 12,
dataState: {
arrays: [
{
id: 'coins',
label: 'Coins',
elements: [
coinElement(0, 1, 'success'),
coinElement(1, 2, 'dimmed'),
coinElement(2, 5, 'success'),
],
},
],
pointers: [],
variables: [
{ id: 'result', name: 'result', value: 3, derivation: '5 + 5 + 1 = 11' },
],
calculations: [
{ id: 'calc-1', expression: '5 + 5 + 1', result: '11', position: 'above' },
],
grids: [
{
id: 'dp',
label: 'DP Table',
colLabels: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
cells: [[
dpCell(0, 0, 'success'),
dpCell(1, 1, 'success'),
dpCell(2, 1, 'success'),
dpCell(3, 2, 'success'),
dpCell(4, 2, 'success'),
dpCell(5, 1, 'success'),
dpCell(6, 2, 'success'),
dpCell(7, 2, 'success'),
dpCell(8, 3, 'success'),
dpCell(9, 3, 'success'),
dpCell(10, 2, 'success'),
dpCell(11, 3, 'highlighted'),
]],
},
],
gridPointers: [
{ id: 'ptr-result', name: 'answer', row: 0, col: 11, color: 'result' },
],
},
},
],
};