华为OD统一考试 - Wonderland

题目描述

Wonderland是小王居住地一家很受欢迎的游乐园。Wonderland目前有4种售票方式,分别为一日票(1天)、三日票(3天)、周票(7天)和月票(30天)。

每种售票方式的价格由一个数组给出,每种票据在票面时限内可以无限制地进行游玩。例如:

小王在第10日买了一张三日票,小王可以在第10日、第11日和第12日进行无限制地游玩。

小王计划在接下来一年多次游玩该游乐园。小王计划地游玩日期将由一个数组给出。

现在,请您根据给出地售票价格数组和小王计划游玩日期数组,返回游玩计划所需要地最低消费。

输入描述

输入为2个数组:

  • 售票价格数组为costs,costs.length = 4,默认顺序为一日票、三日票、周票和月票。
  • 小王计划游玩日期数组为days,1 ≤ days.length ≤ 365,1 ≤ days[i] ≤ 365,默认顺序为升序。

输出描述

完成游玩计划的最低消费。

用例

输入

5 14 30 100

1 3 5 20 21 200 202 230

输出

40

说明

根据售票价格数组和游玩日期数组给出的信息,发现每次去玩的时候买一张一日票是最省钱的,所以小王会卖8张一日票,每张5元,最低花费是40元。

题目解析

本题可以使用动态规划求解。

定义一个dp数组,dp[i] 的含义是:前 i 天,完成所有游玩日的最少花费金额。

dp[i] 可以由前面的 dp状态 推导而来:

  • 如果第 i 天不是游玩日,即第 i 天不需要考虑花钱买票,那么:dp[i] = dp[i-1]
  • 如果第 i 天是游玩日,那么此时有四种选择:
  1. 如果第 i 天被购买的"一日票"有效期覆盖,那么:dp[i] = dp[i-1] + costs[0],其中:dp[i - 1] 是前 i-1 天的花费costs[0] 是第 i 天的花费,即第 i 天买了当天有效的"一日票"
  2. 如果第 i 天被购买的"三日票"有效期覆盖,那么:dp[i] = ( i ≥ 3 ? dp[i-3] : 0 ) + costs[1]我们可以认为在第 i - 2 天购买了"三日票",其有效期覆盖第 i-2 天,第i-1天,第i天,这段时间的花费是 costs[1]而第 i-2 天之前,即:第0天~第i-3天的花费是dp[i-3],此时需要注意 i - 3 可能为负数,如果为负数,则表示第 i-2 天之前没有花费,即为0
  3. 如果第 i 天被购买的"七日票"有效期覆盖,那么:dp[i] = ( i ≥ 7 ?dp[i-7] 0) + costs[2]原理同上
  4. 如果第 i 天被购买的"月票"有效期覆盖,那么:dp[i] = ( i ≥ 30 ?dp[i-30] 0) + costs[3]原理同上

因此,如果第 i 天是游玩日,那么我们可以选择上面四种花费中最小的花费。

最后只需要返回 dp[maxDay] 即可,其中maxDay为最大的游玩日,即为days[days.length - 1]。


import Foundation

func ODTest_2_43() {
    print("输入描述")
    print("售票价格数组为costs,costs.length = 4,默认顺序为一日票、三日票、周票和月票。")
    let costs = (readLine() ?? "").split(separator: " ").map { Int($0) ?? 0 }
    print("小王计划游玩日期数组为days,1 ≤ days.length ≤ 365,1 ≤ days[i] ≤ 365,默认顺序为升序。")
    let days = (readLine() ?? "").split(separator: " ").map { Int($0) ?? 0 }
    // 最大游玩日
    let maxDay = days[days.count - 1]
    // dp[i] 表示 前i天中完成其中所有游玩日需要的最少花费
    var dp: [Int] = Array(repeating: 0, count: maxDay + 1) // dp[0] 默认为 0, 表示前0天花费0元

    // index用于指向当前需要完成的游玩日days[index]
    var index = 0

    // 遍历第1天~第maxDay天
    for i in 1 ... maxDay {
        if i == days[index] {
            // 如果第i天是游玩日,那么此时有四种花费选择
            // 选择买"一日票",该花费仅用于第i天的游玩,那么此时前i天的花费就是 dp[i-1] + cost[0]
            let buy1 = dp[i - 1] + costs[0]
            // 选择买"三日票",该花费可用于第i天,第i-1天,第i-2天(相当于在第i-2天购买),那么此时前i天的花费就是 dp[i-3] + cost[1]
            // 需要注意,如果 i < 3,那么dp[i-3]越界,此时前(i-3)天不存在,即花费为0
            let buy3 = (i >= 3 ? dp[i - 3] : 0) + costs[1]
            // 选择买“七日票”,该花费可用于第i天~第i-6天(相当于在第i-6天购买),那么此时前i天的花费就是 dp[i-7] + cost[2]
            // 同上,注意i<7的处理
            let buy7 = (i >= 7 ? dp[i - 7] : 0) + costs[2]

            // 选择买“月票”,该花费可用于第i天~第i-29天(相当于在第i-29天购买),那么此时前i天的花费就是 dp[i-30] + cost[3]
            // 同上,注意i<30的处理
            let buy30 = (i >= 30 ? dp[i - 30] : 0) + costs[3]
            // 最终dp[i]取上面四种花费的最小值
            dp[i] = min(min(min(buy1, buy3), buy7), buy30)
            // 匹配下一个游玩日(days是升序的,因此index 和 days[index] 正相关)
            index += 1
        } else {
            // 如果第i天不是游玩日,那么第i天不需要花费,即前i天的花费和前i-1天的花费一样
            dp[i] = dp[i - 1]
        }
    }
    print("\(dp[maxDay])")
}

2024华为OD机试卷题 文章被收录于专栏

本专栏给大家提供了华为2024最新华为OD 题目汇总。华为OD机试刷题记录机考算法题库,帮助你上岸华为。提供C++/Java、JavaScript、Python四种语言的解法。

全部评论

相关推荐

1 收藏 评论
分享
牛客网
牛客企业服务