作者:MJ昊
公众号:程序猿的编程之路
今天是 昊 的算法之路第12天,今天分享的是LeetCode第983题最低票价的解题思路。这道题目难度为中等
,是一道典型的动态规划问题。
题目描述简要回顾
给定一个按升序排列的天数数组 days
,表示你需要在这些天进行旅行。此外,还有三种票价和票的有效天数:
- 一日票:
costs[0]
元,有效期 1 天 - 七日票:
costs[1]
元,有效期 7 天 - 三十日票:
costs[2]
元,有效期 30 天
你需要根据每天的出行情况,选择购买票的方式,使得总费用最低。
解题思路
-
动态规划状态定义:
- 使用
dp[i]
表示从第 1 天到第i
天之间的最小总费用。 - 如果当天不需要旅行,则
dp[i] = dp[i - 1]
。 - 如果当天需要旅行,则考虑买不同票的方案,选择最小费用。
- 使用
-
状态转移方程:
- 当天购买一日票:
dp[i] = dp[i - 1] + costs[0]
- 当天购买七日票:
dp[i] = dp[i - 7] + costs[1]
- 当天购买三十日票:
dp[i] = dp[i - 30] + costs[2]
- 对这三种方案取最小值。
- 当天购买一日票:
-
优化存储:
- 使用
Set
存储需要旅行的天数,加快查找效率。 - 遍历从第 1 天到最后一天,用动态规划数组存储结果。
- 使用
代码实现:
var mincostTickets = function (days, costs) { const lastDay = days[days.length - 1]; const dp = new Array(lastDay + 1).fill(0); const daySet = new Set(days); // 使用 Set 提高查找天数的效率 for (let i = 1; i <= lastDay; i++) { if (!daySet.has(i)) { dp[i] = dp[i - 1]; // 如果当天不需要旅行,则费用和前一天相同 } else { // 如果当天需要旅行,计算买不同类型票的最小费用 dp[i] = Math.min( dp[Math.max(0, i - 1)] + costs[0], // 一日票 dp[Math.max(0, i - 7)] + costs[1], // 七日票 dp[Math.max(0, i - 30)] + costs[2], // 三十日票 ); } } return dp[lastDay]; // 返回最后一天的最小费用 };
复杂度分析##
- 时间复杂度:O(n),其中
n
为最后一天的天数。我们需要遍历从第 1 天到lastDay
天的所有天数,每次操作都是 O(1)。 - 空间复杂度:O(n),动态规划数组
dp
需要存储从第 0 天到lastDay
天的费用。
总结
这道题的关键在于理解如何构建 状态转移方程 和 选择最优策略。通过遍历每一天并计算每种票的最小费用,我们可以在最后一天得到最优解。动态规划的思想在于通过解决子问题来构建最终解,这在处理类似问题时非常有效。