题目:
即,给定一个数组代表你一年中的什么时候会去电影院,给定另外一个数组代表一日票,七日票,跟三十日票的金额,要求出如何买票,才能使花费最少。
思路:
明显看出这个是一个动态规划的题目,因此我需要两步曲来完成,首先是状态的定义
,我们发现,比如 days[1,2,3,5],我们要输出的是第5天所花费的最少金额,而且 days是严格递增的,所以我们只要求出第days[days.length - 1]
的花费就OK,因此可以定义状态dp[days[days.length - 1]]
。
定义好了状态,第二步就是看状态的转移方程,由于我们定义的状态是天数,我们需要做的,就是从第0天的花费,递推到第days[days.length - 1]天的花费,而对于每一天,只有两种情况:
① 这一天不去看电影,则到达这一天的花费dp[i]为:dp[i] = dp[i - 1]
,跟前一天的花费是相同的
② 这一天去看电影,则我们要判断这一天需要买什么票,哪种票更合适,即我们判断我们是买一日票,还是过去七天用七日票,还是过去三十天用三十日票的总花费更低,因此判断dp[i - 1] + costs[0], dp[i - 7] + costs[1]), dp[i - 30] + costs[2])
这三个数哪个最小,赋值为当前的dp[i]
代码:
public int mincostTickets(int[] days, int[] costs) {
// 判断输入是否合法
if (days == null || costs == null || costs.length != 3 || days.length == 0) return 0;
// 状态的定义
int[] dp = new int[days[days.length - 1] + 1];
// 将有观影的时间点存入到 set中,便于判断
Set<Integer> set = new HashSet<>();
for (int day : days
) {
set.add(day);
}
// 从第 day[0]天开始遍历,因为前面几日都无观影,花费为 0
for (int i = days[0]; i < dp.length; i++) {
if (set.contains(i)) {
dp[i] = Math.min(Math.min(helper(dp, i - 1) + costs[0], helper(dp, i - 7) + costs[1]),
helper(dp, i - 30) + costs[2]);
} else {
dp[i] = dp[i - 1];
}
}
return dp[days[days.length - 1]];
}
// 处理判断七日票跟三十日票时,可能出现数组越界的情况
private int helper(int[] dp, int i) {
if (i <= 0)
return 0;
return dp[i];
}
在 LeetCode上提交结果:
其他 LeetCode 题目解析