You have planned some train traveling one year in advance. The days of the year in which you will travel are given as an integer array days
. Each day is an integer from 1
to 365
.
Train tickets are sold in three different ways:
costs[0]
dollars,costs[1]
dollars, andcosts[2]
dollars.The passes allow that many days of consecutive travel.
2
, then we can travel for 7
days: 2
, 3
, 4
, 5
, 6
, 7
, and 8
.Return the minimum number of dollars you need to travel every day in the given list of days.
Example 1:
Input: days = [1,4,6,7,8,20], costs = [2,7,15] Output: 11 Explanation: For example, here is one way to buy passes that lets you travel your travel plan: On day 1, you bought a 1-day pass for costs[0] = $2, which covered day 1. On day 3, you bought a 7-day pass for costs[1] = $7, which covered days 3, 4, ..., 9. On day 20, you bought a 1-day pass for costs[0] = $2, which covered day 20. In total, you spent $11 and covered all the days of your travel.
Example 2:
Input: days = [1,2,3,4,5,6,7,8,9,10,30,31], costs = [2,7,15] Output: 17 Explanation: For example, here is one way to buy passes that lets you travel your travel plan: On day 1, you bought a 30-day pass for costs[2] = $15 which covered days 1, 2, ..., 30. On day 31, you bought a 1-day pass for costs[0] = $2 which covered day 31. In total, you spent $17 and covered all the days of your travel.
Constraints:
1 <= days.length <= 365
1 <= days[i] <= 365
days
is in strictly increasing order.costs.length == 3
1 <= costs[i] <= 1000
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to finding the minimum cost for tickets involves checking every possible combination of ticket types we could buy for the given travel days. We consider all the combinations and pick the cheapest one. Basically, we're trying out every single way to buy tickets and finding the cheapest one.
Here's how the algorithm would work step-by-step:
def mincostTicketsBruteForce(days, costs):
number_of_days = len(days)
minimum_cost = float('inf')
def calculate_cost(current_day_index, current_cost):
nonlocal minimum_cost
# If all days are covered, update the minimum cost
if current_day_index == number_of_days:
minimum_cost = min(minimum_cost, current_cost)
return
# Try buying a 1-day pass
calculate_cost(find_next_day(current_day_index, days[current_day_index] + 1), current_cost + costs[0])
# Try buying a 7-day pass
# Explore buying a 7-day pass
calculate_cost(find_next_day(current_day_index, days[current_day_index] + 7), current_cost + costs[1])
# Try buying a 30-day pass
# Explore buying a 30-day pass
calculate_cost(find_next_day(current_day_index, days[current_day_index] + 30), current_cost + costs[2])
def find_next_day(current_day_index, covered_until):
next_day_index = current_day_index + 1
# Find the next travel day not covered by the current ticket
while next_day_index < number_of_days and days[next_day_index] < covered_until:
next_day_index += 1
return next_day_index
calculate_cost(0, 0)
return minimum_cost
The best way to solve this problem is by figuring out the cheapest cost to travel up to each day. We use previous best costs to calculate the cost of traveling on any given day, picking the cheapest option at each step. This avoids recalculating costs and ensures we find the absolute minimum.
Here's how the algorithm would work step-by-step:
def minimum_cost_for_tickets(days, costs):
last_day = days[-1]
minimum_cost = [0] * (last_day + 1)
days_set = set(days)
for day in range(1, last_day + 1):
if day not in days_set:
minimum_cost[day] = minimum_cost[day - 1]
else:
# Consider all ticket options to find the min cost.
minimum_cost[day] = costs[0] + minimum_cost[day - 1]
minimum_cost[day] = min(minimum_cost[day],
costs[1] + minimum_cost[max(0, day - 7)])
# Thirty day pass
minimum_cost[day] = min(minimum_cost[day],
costs[2] + minimum_cost[max(0, day - 30)])
return minimum_cost[last_day]
Case | How to Handle |
---|---|
days is null or empty | Return 0, as no travel is required. |
costs is null or of incorrect size (not 3 elements) | Throw an IllegalArgumentException or return a predefined error code like -1. |
days contains duplicate days | The dynamic programming solution correctly handles duplicate days since only the presence or absence of a day affects the result. |
days contains non-positive integers (<=0) | Consider these invalid inputs and throw an IllegalArgumentException, as days should represent positive calendar days. |
days is a very large array (e.g., 365 days) | Dynamic programming solution's space complexity is O(max(days)), so it might consume considerable memory but should still be solvable. |
costs contains very large values (approaching Integer.MAX_VALUE) | Potential integer overflow during calculations, so use long data types to store intermediate cost values. |
days is sorted in descending order | Sort the `days` array in ascending order as dynamic programming solution generally assumes that days are visited in increasing order. |
days contains values beyond a reasonable time frame (e.g., year 10000) | Limit `days` value range to prevent excessive memory usage and/or integer overflow when indexing (e.g., maximum day being 365 for a year) and throw IllegalArgumentException if exceeded. |