作者: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天的费用。
总结
这道题的关键在于理解如何构建 状态转移方程 和 选择最优策略。通过遍历每一天并计算每种票的最小费用,我们可以在最后一天得到最优解。动态规划的思想在于通过解决子问题来构建最终解,这在处理类似问题时非常有效。