Python/dynamic_programming/minimum_tickets_cost.py
Caeden Perelli-Harris 598f6a26a1
refactor: Condense password related files in one (#7939)
* refactor: Condense `password` related files in one

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Update other/password.py

Co-authored-by: Christian Clauss <cclauss@me.com>

* dynamic_programming

* test: Make test input `str`

* requirements.txt: Remove cython>=0.29.28  # For statsmodels on Python 3.11

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Christian Clauss <cclauss@me.com>
2022-11-02 17:20:57 +01:00

130 lines
3.6 KiB
Python

"""
Author : Alexander Pantyukhin
Date : November 1, 2022
Task:
Given a list of days when you need to travel. Each day is integer from 1 to 365.
You are able to use tickets for 1 day, 7 days and 30 days.
Each ticket has a cost.
Find the minimum cost you need to travel every day in the given list of days.
Implementation notes:
implementation Dynamic Programming up bottom approach.
Runtime complexity: O(n)
The implementation was tested on the
leetcode: https://leetcode.com/problems/minimum-cost-for-tickets/
Minimum Cost For Tickets
Dynamic Programming: up -> down.
"""
from functools import lru_cache
def mincost_tickets(days: list[int], costs: list[int]) -> int:
"""
>>> mincost_tickets([1, 4, 6, 7, 8, 20], [2, 7, 15])
11
>>> mincost_tickets([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 30, 31], [2, 7, 15])
17
>>> mincost_tickets([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 30, 31], [2, 90, 150])
24
>>> mincost_tickets([2], [2, 90, 150])
2
>>> mincost_tickets([], [2, 90, 150])
0
>>> mincost_tickets('hello', [2, 90, 150])
Traceback (most recent call last):
...
ValueError: The parameter days should be a list of integers
>>> mincost_tickets([], 'world')
Traceback (most recent call last):
...
ValueError: The parameter costs should be a list of three integers
>>> mincost_tickets([0.25, 2, 3, 4, 5, 6, 7, 8, 9, 10, 30, 31], [2, 90, 150])
Traceback (most recent call last):
...
ValueError: The parameter days should be a list of integers
>>> mincost_tickets([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 30, 31], [2, 0.9, 150])
Traceback (most recent call last):
...
ValueError: The parameter costs should be a list of three integers
>>> mincost_tickets([-1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 30, 31], [2, 90, 150])
Traceback (most recent call last):
...
ValueError: All days elements should be greater than 0
>>> mincost_tickets([2, 367], [2, 90, 150])
Traceback (most recent call last):
...
ValueError: All days elements should be less than 366
>>> mincost_tickets([2, 3, 4, 5, 6, 7, 8, 9, 10, 30, 31], [])
Traceback (most recent call last):
...
ValueError: The parameter costs should be a list of three integers
>>> mincost_tickets([], [])
Traceback (most recent call last):
...
ValueError: The parameter costs should be a list of three integers
>>> mincost_tickets([2, 3, 4, 5, 6, 7, 8, 9, 10, 30, 31], [1, 2, 3, 4])
Traceback (most recent call last):
...
ValueError: The parameter costs should be a list of three integers
"""
# Validation
if not isinstance(days, list) or not all(isinstance(day, int) for day in days):
raise ValueError("The parameter days should be a list of integers")
if len(costs) != 3 or not all(isinstance(cost, int) for cost in costs):
raise ValueError("The parameter costs should be a list of three integers")
if len(days) == 0:
return 0
if min(days) <= 0:
raise ValueError("All days elements should be greater than 0")
if max(days) >= 366:
raise ValueError("All days elements should be less than 366")
days_set = set(days)
@lru_cache(maxsize=None)
def dynamic_programming(index: int) -> int:
if index > 365:
return 0
if index not in days_set:
return dynamic_programming(index + 1)
return min(
costs[0] + dynamic_programming(index + 1),
costs[1] + dynamic_programming(index + 7),
costs[2] + dynamic_programming(index + 30),
)
return dynamic_programming(1)
if __name__ == "__main__":
import doctest
doctest.testmod()