2024华为OD笔试机试 - Wonderland (python/c++/java D卷C卷真题算法)

华为OD机试(C卷+D卷)2024真题目录(Java & c++ & python)

题目描述

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[i] 表示:前 i 天,完成所有游玩日的最少花费金额。

  • 如果第 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

  1. 如果第 i 天被购买的"七日票"有效期覆盖,那么:dp[i] = ( i ≥ 7 ?dp[i-7] : 0) + costs[2]

  2. 如果第 i 天被购买的"月票"有效期覆盖,那么:dp[i] = ( i ≥ 30 ?dp[i-30] : 0) + costs[3]

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

最后只需要返回 days[days.length - 1] 即可

C++、Java、Python代码如下:

C++参考代码

#include <bits/stdc++.h>

using namespace std;

int main() {
    // 读取四种票的费用
    int costs[4];
    for (int & cost : costs) {
        cin >> cost;
    }

    vector<int> days;
    int day;
    while (cin >> day) {
        days.push_back(day);
    }

    // 最大游玩日
    int maxDay = days.back();

    // dp[i] 表示前i天中完成其中所有游玩日需要的最少花费
    vector<int> dp(maxDay + 1, 0);

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

    // 遍历第1天~第maxDay天
    for (int i = 1; i <= maxDay; i++) {
        if (i == days[index]) {
            // 四种选择的花费
            int buy1 = dp[i - 1] + costs[0];
            int buy3 = (i >= 3 ? dp[i - 3] : 0) + costs[1];
            int buy7 = (i >= 7 ? dp[i - 7] : 0) + costs[2];
            int buy30 = (i >= 30 ? dp[i - 30] : 0) + costs[3];

            // 选择最小花费
            dp[i] = min({buy1, buy3, buy7, buy30});

            // 匹配下一个游玩日
            index++;
        } else {
            dp[i] = dp[i - 1];
        }
    }

    // 输出最小花费
    cout << dp[maxDay] << endl;

    return 0;
}

Java参考代码

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int[] costs = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
        int[] days = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();

        // 最大游玩日
        int maxDay = days[days.length - 1];

        // dp[i] 表示前i天中完成所有游玩日所需的最小花费
        int[] dp = new int[maxDay + 1];

        int index = 0;

        // 遍历第1天到第maxDay天
        for (int i = 1; i <= maxDay; i++) {
            if (i == days[index]) {
                // 如果第i天是游玩日,则有四种票价选择

                // 选择"一日票",只用于第i天
                int buy1 = dp[i - 1] + costs[0];

                // 选择"三日票",用于第i天、第i-1天和第i-2天
                int buy3 = (i >= 3 ? dp[i - 3] : 0) + costs[1];

                // 选择"七日票",用于第i天至第i-6天
                int buy7 = (i >= 7 ? dp[i - 7] : 0) + costs[2];

                // 选择"月票",用于第i天至第i-29天
                int buy30 = (i >= 30 ? dp[i - 30] : 0) + costs[3];

                // 取四种方案中的最小值
                dp[i] = Math.min(Math.min(buy1, buy3), Math.min(buy7, buy30));

                // 移动到下一个游玩日
                index++;
            } else {
                // 非游玩日时花费与前一天相同
                dp[i] = dp[i - 1];
            }
        }

        // 输出最小花费
        System.out.println(dp[maxDay]);
    }
}

Python参考代码

# 输入获取
costs = list(map(int, input().split()))
days = list(map(int, input().split()))

# 最大游玩日
max_day = days[-1]

# dp[i] 表示 前i天中完成其中所有游玩日需要的最少花费
dp = [0] * (max_day + 1)  # dp[0] 默认为 0, 表示前0天花费0元

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

# 遍历第1天~第max_day天
for i in range(1, max_day + 1):
    if i == days[day_index]:
        # 如果第i天是游玩日,那么此时有四种花费选择

        # 选择买"一日票"
        buy_1_day = dp[i - 1] + costs[0]

        # 选择买"三日票"
        buy_3_days = (dp[i - 3] if i >= 3 else 0) + costs[1]

        # 选择买“七日票”
        buy_7_days = (dp[i - 7] if i >= 7 else 0) + costs[2]

        # 选择买“月票”
        buy_30_days = (dp[i - 30] if i >= 30 else 0) + costs[3]

        # 最终dp[i]取上面四种花费的最小值
        dp[i] = min(buy_1_day, buy_3_days, buy_7_days, buy_30_days)

        # 匹配下一个游玩日
        day_index += 1
    else:
        # 如果第i天不是游玩日,那么第i天不需要花费,即前i天的花费和前i-1天的花费一样
        dp[i] = dp[i - 1]

# 输出最终的最少花费
print(dp[max_day])

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

算法之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值