aboutsummaryrefslogtreecommitdiff
path: root/challenge-285/jtimothyking/csharp/ch-2.cs
blob: eecd7616ec21a7b61288d9d0b1899d6bbd1fda8b (plain)
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using System.Collections.Concurrent;

namespace _2_making_change;

public static class MakingChange
{
    private static readonly int[] Coins = [1, 5, 10, 25, 50];
    private static readonly ConcurrentDictionary<ChangeKey, Task<int>> NumWaysTasks = new();

    public static async Task Main()
    {
        var amount = int.Parse(await Console.In.ReadLineAsync() ?? "");
        var numWays = await GetNumWaysToMakeChangeAsync(amount);
        Console.WriteLine(numWays);
    }

    private static Task<int> GetNumWaysToMakeChangeAsync(int amount) =>
        GetNumWaysToMakeChangeAsync(amount, Coins.Length);

    private static Task<int> GetNumWaysToMakeChangeAsync(int amount, int numCoins)
    {
        // Calculate the number of ways to make change using recursion.

        // Parallelize and memoize by storing a task for each recursive call.
        // Each call to CalculateAsync creates a new task, which is executed in a
        // worker thread managed by the .NET thread pool. This task is stored in
        // the NumWays concurrent dictionary, and awaiting it returns the result.
        return NumWaysTasks.GetOrAdd(new ChangeKey(amount, numCoins), CalculateAsync);

        async Task<int> CalculateAsync(ChangeKey key)
        {
            if (key.Amount == 0) return 1;
            if (key.Amount < 0 || key.NumCoins == 0) return 0;
            // Execute the recursive calls in parallel.
            var useCoinTask = GetNumWaysToMakeChangeAsync(key.Amount - Coins[key.NumCoins - 1], key.NumCoins);
            var skipCoinTask = GetNumWaysToMakeChangeAsync(key.Amount, key.NumCoins - 1);
            return await useCoinTask + await skipCoinTask;
        }
    }

    private record struct ChangeKey(int Amount, int NumCoins);
}