美文网首页动态规划动态规划
【DP】983. Minimum Cost For Ticket

【DP】983. Minimum Cost For Ticket

作者: 牛奶芝麻 | 来源:发表于2019-04-30 21:21 被阅读0次

    问题描述:

    In a country popular for train travel, you have planned some train travelling one year in advance. The days of the year that you will travel is given as an array days. Each day is an integer from 1 to 365.

    Train tickets are sold in 3 different ways:

    a 1-day pass is sold for costs[0] dollars;
    a 7-day pass is sold for costs[1] dollars;
    a 30-day pass is sold for costs[2] dollars.
    

    The passes allow that many days of consecutive travel. For example, if we get a 7-day pass on day 2, then we can travel for 7 days: day 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.
    
    Note:
    1 <= days.length <= 365
    1 <= days[i] <= 365
    days is in strictly increasing order.
    costs.length == 3
    1 <= costs[i] <= 1000
    

    解题思路:

    很显现这是一道动态规划的问题,而且很容易想到用 dp[i] 表示第 i 天累计的最少费用。而问题的关键在于寻找转移方程式,然后更新 dp[i],得出的 dp[N] 就是最后的答案。

    这道题如果眼光受限于给定的列表中的天数,寻找转移方程会比较困难。较为简单的一种方法是,把没有出现的天数也考虑在内,如果某天没有在列表中出现,那么费用不用更新,即 dp[i] = dp[i - 1], if i is not in days这样做的好处是,时间可以连续,保证了 dp[i] 在每一天都有值,便于后续的转移方程的推导。

    现在考虑,如果某一天 i 在 days 中,即 i is in days 的情况。因为在计算到第 i 天的费用时,前 i - 1 天我们已经求出了,而且是最优解,所以在计算 dp[i] 时可以利用前 i - 1天的结果。

    以 days = [1,2,4,7,8], costs = [2,7,15] 为例,我们容易求得 dp[0] = 0 (第0天他花费为0),dp[1] = 2, dp[2] = dp[2-1] + cost[0] = 4, dp[3] = dp[3-1] = 4(没有出现的天数), dp[4] = dp[4-1] + cost[0] = 6, dp[5] = dp[5-1] = 6(没有出现的天数), dp[6] = dp[6-1] = 6(没有出现的天数), 而 dp[7] = min(dp[7-1] + cost[0], dp[7-7] + cost[1]) = min(8, 0+7) = 7, dp[8] = min(dp[8-1] + cost[0], dp[8-7] + cost[1]) = min(7+2, 2+7) = 9

    观察到规律,第 i 天的费用取决于 dp[i - 1] + cost[0]、dp[i - 7] + cost[1]、dp[i - 30] + cost[2] 中的最小值,即 dp[i] = min(dp[i - 1] + cost[0], dp[i - 7] + cost[1], dp[i - 30] + cost[2])

    因此,只需要遍历小于等于 days[-1] 的所有天数,依次求得 dp[i],最终 dp[days[-1]] 就是最后的答案。

    如果把 days 变成集合 set,在查找的时候,时间复杂度为 O(1),因此整体时间复杂度为 O(n)。

    Python3 实现:

    class Solution:
        def mincostTickets(self, days, costs):
            dp = [0] * 366
            days_set = set(days)  # 变成集合,后面查找的时间复杂度为 O(1)
            for i in range(1, days[-1] + 1):
                if i not in days_set:
                    dp[i] = dp[i - 1]
                else:
                    dp[i] = min(dp[i - 1] + costs[0], dp[i - 7] + costs[1], dp[i - 30] + costs[2])
            return dp[days[-1]]
    
    print(Solution().mincostTickets([1,4,6,7,8,20], [2,7,15]))  # 11
    print(Solution().mincostTickets([1,2,3,4,5,6,7,8,9,10,30,31], [2,7,15]))  # 17
    print(Solution().mincostTickets([1,4,6,7,8,20], [7,2,15]))  # 6
    print(Solution().mincostTickets([1,2,3,4,6,8,9,10,13,14,16,17,19,21,24,26,27,28,29], [3,14,50]))  # 50
    

    相关文章

      网友评论

        本文标题:【DP】983. Minimum Cost For Ticket

        本文链接:https://www.haomeiwen.com/subject/wldbnqtx.html