通过率 64% · 提交 404 · 通过 257
小慕最近接手了一个游乐园的票务优化项目。这个游乐园提供4种门票:一日票(1天)、三日票(3天)、周票(7天)和月票(30天)。 每种门票的价格由一个数组给出,在票面有效期内可以无限次入园游玩。 例如,小慕在第10天购买了一张三日票,那么他可以在第10天、第11天和第12天任意进出游乐园。小慕计划在未来一年内多次前往这个游乐园。 小慕的游玩日期由一个数组给出。现在,请你根据门票价格数组和小慕的游玩日期数组,计算出完成所有游玩计划所需的最低总花费。
这类题属于华为 OD 机考真题方向中「200分 / DP」方向的高频题型,通常考察对「200分 / DP」的建模能力与边界条件处理。掌握本题的解题思路后,可举一反三应对同类真题方向,稳步提升机考通过率。
提示:带虚线的词点一下有通俗解释。
输入为 2 个数组:
售票价格数组为 costs,costs.length = 4,默认顺序为一日票、三日票、周票和月票。
小王计划游玩日期数组为 days,1 ≤ days.length ≤ 365,1 ≤ days[i] ≤ 365,默认顺序为升序。
完成游玩计划的最低消费。
示例 1
输入示例
5 14 30 100 1 3 5 20 21 200 202 230
输出示例
40
根据售票价格数组和游玩日期数组给出的信息,发现每次去玩的时候买一张一日票是最省钱的,所以小王会卖 8 张一日票,每张 5 元,最低花费是 40 元。
示例 2
输入示例
1 2 7 25 1 4 6 7 8
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。
令 n 表示游玩的次数。令 times = [1, 3, 7, 30],和 cost 数组相对应,表示一张门票的有效期。 我们只能一天天旅游,只能过完前面的日子才能过后面的日子。
dp 数组是一个长度为 n+1 的一维列表。 dp[i] 表示以第 i 次游玩完成后所需要花费的最小价格。注意这里假设 days 数组从下标 1 开始。 dp[n] 即为答案。
计算 dp[i] 的值。 对于前 i 个次游玩,假设 j <= i,如果第 j 次游玩到第 i 次游玩之间的天数可以由第 k(0、1、2、3)种票完全覆盖(即 days[i] - days[j] + 1 <= times[k]),那么说明这段旅程可以花费 cost[k] 购买第 k 种票,因此有:
即从第 j-1 次游玩转移而来。
举个例子,存在 days = [0, 1, 4, 6, 8] 天都要游玩,那么第 8 天的游玩的情况即 dp[4] 的计算:
days[4] 开始用某种票能够覆盖 8-8 天这个区间,可以购买 1、3、7、30 日票来完成,那么应该在第 6 天的基础上即 dp[3] 转移过来。days[3] 开始用某种票能够覆盖 6-8 这个区间,可以购买 3、7、30 日票来完成,那么应该在第 4 天的基础上即 dp[2] 转移过来。dp[1] 转移过来。dp[0] 转移过来。对于第 i 次游玩,选择一个最佳的覆盖方式即可,因此反复地取最小值,即:
对应的代码框架如下:
一开始没有进行任何游玩,因此 dp[0] = 0;后来的所有值初始化成无穷大即可。
复杂度分析 设 n 为游玩日期数组的长度,即小慕需要入园的总次数(题面说明游玩计划在一年之内,n 不会超过一年的天数)。
补充说明:由于票的有效期最长为 30 天,当 days[i] - days[j] + 1 > 30 时四种票都无法覆盖,内层的 j 其实可以提前终止来剪枝;但在本题一年以内的数据规模下,O(n^2) 的朴素写法已经足够。
登录后可查看你在本题的历史提交,以及每次的各用例通过情况。
输出示例
4
时间限制 1000 ms · 内存限制 128 MB