Task: Minimal steps down to 1



C#: using System;
using System.Collections.Generic;
using System.Linq;

public static class MinimalSteps
{
    public static void Main() {
        var (divisors, subtractors) = (new int[] { 2, 3 }, new [] { 1 });
        var lookup = CreateLookup(2_000, divisors, subtractors);
        Console.WriteLine($"Divisors: [{divisors.Delimit()}], Subtractors: [{subtractors.Delimit()}]");
        PrintRange(lookup, 10);
        PrintMaxMins(lookup);
        lookup = CreateLookup(20_000, divisors, subtractors);
        PrintMaxMins(lookup);
        Console.WriteLine();

        subtractors = new [] { 2 };
        lookup = CreateLookup(2_000, divisors, subtractors);
        Console.WriteLine($"Divisors: [{divisors.Delimit()}], Subtractors: [{subtractors.Delimit()}]");
        PrintRange(lookup, 10);
        PrintMaxMins(lookup);
        lookup = CreateLookup(20_000, divisors, subtractors);
        PrintMaxMins(lookup);
    }

    private static void PrintRange((char op, int param, int steps)[] lookup, int limit) {
        for (int goal = 1; goal <= limit; goal++) {
            var x = lookup[goal];
            if (x.param == 0) {
                Console.WriteLine($"{goal} cannot be reached with these numbers.");
                continue;
            }
            Console.Write($"{goal} takes {x.steps} {(x.steps == 1 ? "step" : "steps")}: ");
            for (int n = goal; n > 1; ) {
                Console.Write($"{n},{x.op}{x.param}=> ");
                n = x.op == '/' ? n / x.param : n - x.param;
                x = lookup[n];
            }
            Console.WriteLine("1");
        }
    }

    private static void PrintMaxMins((char op, int param, int steps)[] lookup) {
        var maxSteps = lookup.Max(x => x.steps);
        var items = lookup.Select((x, i) => (i, x)).Where(t => t.x.steps == maxSteps).ToList();
        Console.WriteLine(items.Count == 1
            ? $"There is one number below {lookup.Length-1} that requires {maxSteps} steps: {items[0].i}"
            : $"There are {items.Count} numbers below {lookup.Length-1} that require {maxSteps} steps: {items.Select(t => t.i).Delimit()}"
        );
    }

    private static (char op, int param, int steps)[] CreateLookup(int goal, int[] divisors, int[] subtractors)
    {
        var lookup = new (char op, int param, int steps)[goal+1];
        lookup[1] = ('/', 1, 0);
        for (int n = 1; n < lookup.Length; n++) {
            var ln = lookup[n];
            if (ln.param == 0) continue;
            for (int d = 0; d < divisors.Length; d++) {
                int target = n * divisors[d];
                if (target > goal) break;
                if (lookup[target].steps == 0 || lookup[target].steps > ln.steps) lookup[target] = ('/', divisors[d], ln.steps + 1);
            }
            for (int s = 0; s < subtractors.Length; s++) {
                int target = n + subtractors[s];
                if (target > goal) break;
                if (lookup[target].steps == 0 || lookup[target].steps > ln.steps) lookup[target] = ('-', subtractors[s], ln.steps + 1);
            }
        }
        return lookup;
    }

    private static string Delimit<T>(this IEnumerable<T> source) => string.Join(", ", source);
}


Go: package main

import (
    "fmt"
    "strings"
)

const limit = 50000

var (
    divs, subs []int
    mins       [][]string
)


func minsteps(n int) {
    if n == 1 {
        mins[1] = []string{}
        return
    }
    min := limit
    var p, q int
    var op byte
    for _, div := range divs {
        if n%div == 0 {
            d := n / div
            steps := len(mins[d]) + 1
            if steps < min {
                min = steps
                p, q, op = d, div, '/'
            }
        }
    }
    for _, sub := range subs {
        if d := n - sub; d >= 1 {
            steps := len(mins[d]) + 1
            if steps < min {
                min = steps
                p, q, op = d, sub, '-'
            }
        }
    }
    mins[n] = append(mins[n], fmt.Sprintf("%c%d -> %d", op, q, p))
    mins[n] = append(mins[n], mins[p]...)
}

func main() {
    for r := 0; r < 2; r++ {
        divs = []int{2, 3}
        if r == 0 {
            subs = []int{1}
        } else {
            subs = []int{2}
        }
        mins = make([][]string, limit+1)
        fmt.Printf("With: Divisors: %v, Subtractors: %v =>\n", divs, subs)
        fmt.Println("  Minimum number of steps to diminish the following numbers down to 1 is:")
        for i := 1; i <= limit; i++ {
            minsteps(i)
            if i <= 10 {
                steps := len(mins[i])
                plural := "s"
                if steps == 1 {
                    plural = " "
                }
                fmt.Printf("    %2d: %d step%s: %s\n", i, steps, plural, strings.Join(mins[i], ", "))
            }
        }
        for _, lim := range []int{2000, 20000, 50000} {
            max := 0
            for _, min := range mins[0 : lim+1] {
                m := len(min)
                if m > max {
                    max = m
                }
            }
            var maxs []int
            for i, min := range mins[0 : lim+1] {
                if len(min) == max {
                    maxs = append(maxs, i)
                }
            }
            nums := len(maxs)
            verb, verb2, plural := "are", "have", "s"
            if nums == 1 {
                verb, verb2, plural = "is", "has", ""
            }
            fmt.Printf("  There %s %d number%s in the range 1-%d ", verb, nums, plural, lim)
            fmt.Printf("that %s maximum 'minimal steps' of %d:\n", verb2, max)
            fmt.Println("   ", maxs)
        }
        fmt.Println()
    }
}