diff options
| author | Mohammad Sajid Anwar <mohammad.anwar@yahoo.com> | 2024-09-08 11:15:05 +0100 |
|---|---|---|
| committer | Mohammad Sajid Anwar <mohammad.anwar@yahoo.com> | 2024-09-08 11:15:05 +0100 |
| commit | 2c5cab7c611241e0d85eebc4e5e6d89830b7c80b (patch) | |
| tree | 1043adc76fe429b1b537ef574e75525dfe482e17 /challenge-285/ppentchev/python | |
| parent | 7cc7c2044811d44820194e9b8e4ef92a0a80a497 (diff) | |
| download | perlweeklychallenge-club-2c5cab7c611241e0d85eebc4e5e6d89830b7c80b.tar.gz perlweeklychallenge-club-2c5cab7c611241e0d85eebc4e5e6d89830b7c80b.tar.bz2 perlweeklychallenge-club-2c5cab7c611241e0d85eebc4e5e6d89830b7c80b.zip | |
- Added solutions by Matthias Muth.
- Added solutions by Peter Pentchev.
Diffstat (limited to 'challenge-285/ppentchev/python')
| -rw-r--r-- | challenge-285/ppentchev/python/src/perl_weekly_285/ch-1.py | 50 | ||||
| -rw-r--r-- | challenge-285/ppentchev/python/src/perl_weekly_285/ch-2.py | 88 |
2 files changed, 138 insertions, 0 deletions
diff --git a/challenge-285/ppentchev/python/src/perl_weekly_285/ch-1.py b/challenge-285/ppentchev/python/src/perl_weekly_285/ch-1.py new file mode 100644 index 0000000000..51e19e556e --- /dev/null +++ b/challenge-285/ppentchev/python/src/perl_weekly_285/ch-1.py @@ -0,0 +1,50 @@ +# SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net> +# SPDX-License-Identifier: BSD-2-Clause +"""Solve the first task in Perl weekly challenge 285, "No Connection".""" + +from __future__ import annotations + +import dataclasses +import typing + +from . import defs + + +if typing.TYPE_CHECKING: + from typing import Final + + +@dataclasses.dataclass +class NoLeafError(defs.NoSolutionError): + """No leaf destinations at all.""" + + def __str__(self) -> str: + """Provide a human-readable error message.""" + return "No leaf destinations could be found" + + +@dataclasses.dataclass +class MultipleLeavesError(defs.NoSolutionError): + """More than one leaf destination.""" + + multiple: list[str] + """The leaf destinations found.""" + + def __str__(self) -> str: + """Provide a human-readable error message.""" + return f"Multiple leaf destinations found: {self.multiple!r}" + + +def solve_no_connection(routes: list[tuple[str, str]]) -> str: + """Solve the first task, "No Connection.""" + loc_from: Final = {item[0] for item in routes} + loc_to: Final = {item[1] for item in routes} + match sorted(loc_to - loc_from): + case []: + raise NoLeafError + + case [single]: + return single + + case multiple: + raise MultipleLeavesError(multiple) diff --git a/challenge-285/ppentchev/python/src/perl_weekly_285/ch-2.py b/challenge-285/ppentchev/python/src/perl_weekly_285/ch-2.py new file mode 100644 index 0000000000..d2cc783794 --- /dev/null +++ b/challenge-285/ppentchev/python/src/perl_weekly_285/ch-2.py @@ -0,0 +1,88 @@ +# SPDX-FileCopyrightText: Peter Pentchev <roam@ringlet.net> +# SPDX-License-Identifier: BSD-2-Clause +"""Solve the second task in Perl weekly challenge 285, "Making Change".""" + +from __future__ import annotations + +import enum +import itertools +import typing + + +if typing.TYPE_CHECKING: + from typing import Final + + +class Coin(enum.IntEnum): + """A single denomination for breaking change down.""" + + PENNY = 1 + NICKEL = 5 + DIME = 10 + QUARTER = 25 + HALF_DOLLAR = 50 + + def lower(self) -> Coin: + """Return the next lower denomination.""" + match self: + case value if value == Coin.PENNY: + raise RuntimeError(repr(self)) + + case value if value == Coin.NICKEL: + return Coin.PENNY + + case value if value == Coin.DIME: + return Coin.NICKEL + + case value if value == Coin.QUARTER: + return Coin.DIME + + case value if value == Coin.HALF_DOLLAR: + return Coin.QUARTER + + +_ORDER_ABP: Final = [Coin.HALF_DOLLAR, Coin.QUARTER, Coin.DIME, Coin.NICKEL] +"""The denominations in descending order.""" + + +def break_down_ways(amount: int, highest: Coin) -> list[tuple[int, Coin]]: + """Make sure of things and stuff.""" + + def process_single(high: Coin) -> list[tuple[int, Coin]]: + """Get the candidates if we use this coin.""" + lower: Final = high.lower() + return [(amount - mult, lower) for mult in range(high, amount + 1, high)] + + return list(itertools.chain(*(process_single(high) for high in _ORDER_ABP if high <= highest))) + + +def make_change_rec(amount: int, highest: Coin, cache: dict[tuple[int, Coin], int]) -> int: + """Let me count the ways, recursively.""" + + def trivial(amt: int, high: Coin) -> bool: + """Sometimes there is only one.""" + return amt == 0 or high == Coin.PENNY + + if trivial(amount, highest): + return 1 + + cached: Final = cache.get((amount, highest)) + if cached is not None: + return cached + + def process_single(amt: int, high: Coin) -> int: + """Recursively count fewer ways.""" + if trivial(amt, high): + return 1 + + return make_change_rec(amt, high, cache) + + ways: Final = break_down_ways(amount, highest) + total: Final = 1 + sum(itertools.starmap(process_single, ways)) + cache[amount, highest] = total + return total + + +def solve_making_change(amount: int) -> int: + """Let me count the ways.""" + return make_change_rec(amount, Coin.HALF_DOLLAR, {}) |
