Post

Dynamic Programming: Coin Change — Kotlin Solution

Dynamic Programming: Coin Change — Kotlin Solution

Problem Info

  
LeetCode #322 — Coin Change
DifficultyMedium
TopicDynamic Programming, BFS

Given an array of coin denominations and a target amount, return the fewest number of coins needed to make up that amount. Return -1 if it cannot be made.

Example:

1
2
3
4
5
Input:  coins = [1, 5, 10], amount = 11
Output: 2   // 10 + 1

Input:  coins = [2], amount = 3
Output: -1  // cannot make 3 with only 2s

Approach

Build a dp array where dp[i] = minimum coins to make amount i.

Start with dp[0] = 0 (zero coins to make amount 0). For every amount from 1 to amount, try every coin. If amount - coin >= 0, you can use this coin: dp[i] = min(dp[i], dp[i - coin] + 1)

Walk through coins=[1,5,10], amount=11:

1
2
3
4
5
dp[0]  = 0
dp[1]  = min(dp[0]+1) = 1          (use coin 1)
dp[5]  = min(dp[4]+1, dp[0]+1) = 1 (use coin 5)
dp[10] = min(dp[9]+1, dp[5]+1, dp[0]+1) = 1  (use coin 10)
dp[11] = min(dp[10]+1, dp[6]+1, dp[1]+1) = 2 (use coin 1: dp[10]+1=2) ✓

Kotlin Solution

Approach 1 — Bottom-up DP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun coinChange(coins: IntArray, amount: Int): Int {
    val dp = IntArray(amount + 1) { amount + 1 }  // init to "infinity"
    dp[0] = 0

    for (i in 1..amount) {
        for (coin in coins) {
            if (coin <= i) {
                dp[i] = minOf(dp[i], dp[i - coin] + 1)
            }
        }
    }

    return if (dp[amount] > amount) -1 else dp[amount]
}

Approach 2 — BFS (treat as shortest path)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
fun coinChange(coins: IntArray, amount: Int): Int {
    if (amount == 0) return 0

    val visited = BooleanArray(amount + 1)
    val queue = ArrayDeque<Int>()
    queue.add(0)
    visited[0] = true
    var steps = 0

    while (queue.isNotEmpty()) {
        steps++
        repeat(queue.size) {
            val current = queue.removeFirst()
            for (coin in coins) {
                val next = current + coin
                if (next == amount) return steps
                if (next < amount && !visited[next]) {
                    visited[next] = true
                    queue.add(next)
                }
            }
        }
    }

    return -1
}

Why Kotlin Shines Here

IntArray(amount + 1) { amount + 1 } — initialise all values in one line:

1
2
3
val dp = IntArray(amount + 1) { amount + 1 }
// Lambda initialiser sets every element to amount+1 (acts as infinity)
// vs Java: int[] dp = new int[amount + 1]; Arrays.fill(dp, amount + 1);

minOf() — cleaner than Math.min:

1
dp[i] = minOf(dp[i], dp[i - coin] + 1)

1..amount — inclusive range, reads naturally:

1
2
for (i in 1..amount)
// vs Java: for (int i = 1; i <= amount; i++)

repeat(queue.size) — process one BFS level at a time:

1
2
repeat(queue.size) { ... }
// Captures queue.size before the loop starts — correct level-by-level BFS

Why amount + 1 as Infinity?

1
val dp = IntArray(amount + 1) { amount + 1 }

The maximum number of coins to make amount is amount itself (using all 1s). So amount + 1 is safely larger than any valid answer — acts as infinity. At the end, if dp[amount] > amount it means no solution was found → return -1.


Two Approaches Compared

ApproachTimeSpaceNotes
Bottom-up DPO(amount × coins)O(amount)Standard, easy to explain
BFSO(amount × coins)O(amount)Finds shortest path naturally

Complexity

  
TimeO(amount × number of coins)
SpaceO(amount)

Key Takeaway

Build up the answer for every amount from 0 to target. For each amount, try every coin — if it fits, can it give you fewer coins? Use amount + 1 as a safe “infinity” initialiser.

🔗 Solve it on LeetCode →


📚 Kotlin DSA Series

This post is part of the Kotlin DSA series — solving LeetCode problems using idiomatic Kotlin.

← View Full Series Index
This post is licensed under CC BY 4.0 by the author.