华为OD机试真题---最大报酬

题目描述

小明每周上班都会拿到自己的工作清单,工作清单内包含n项工作,每项工作都有对应的耗时时间(单位h)和报酬。工作的总报酬为所有已完成工作的报酬之和。请帮小明安排工作,保证小明在指定的工作时间内工作收入最大化。

输入:

  • 第一行为两个正整数T, n。T代表工作时长(单位h, 0 < T < 1000000),n代表工作数量(1 < n ≤ 3000)。
  • 接下来是n行,每行包含两个整数t, w。t代表该工作消耗的时长(单位h, t > 0),w代表该项工作的报酬。

输出:

  • 小明在指定工作时长内工作可获得的最大报酬。

解题思路

这是一个典型的01背包问题,可以使用动态规划来解决。我们定义一个数组dp,其中dp[i]表示在工作时长为i时可以获得的最大报酬。通过遍历每个工作,并更新dp数组的值,最终dp[T]即为在最大工作时长T内可以获得的最大报酬。

解题步骤

  1. 初始化

    • 创建一个大小为T+1的数组dp,所有元素初始化为0。dp[0]表示在工作时间为0时,最大报酬为0。
  2. 遍历工作

    • 对于每个工作(t, w),其中t为耗时,w为报酬,从T开始向下遍历到t(即从大到小遍历,保证每个工作只被考虑一次)。
    • 更新dp[i]的值:dp[i] = max(dp[i], dp[i-t] + w)。这表示如果当前工作被选择,则当前的最大报酬为不选择该工作时的最大报酬dp[i]与选择该工作后的报酬dp[i-t] + w中的较大值。
  3. 输出结果

    • 遍历结束后,dp[T]即为在最大工作时长T内可以获得的最大报酬,输出dp[T]。

示例代码(Java)

package cn.gov.test.gt4.swjggl.leetcode;

import java.util.Scanner;

public class MaxReward {

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

        // 读取输入
        System.out.println("请输入工作时长T和工作数量n(用空格分隔):");
        int T = scanner.nextInt();
        int n = scanner.nextInt();

        // 初始化工作数组
        int[][] works = new int[n][2];
        System.out.println("请输入每项工作的耗时和报酬(每行两个数,用空格分隔):");
        for (int i = 0; i < n; i++) {
            works[i][0] = scanner.nextInt(); // 耗时
            works[i][1] = scanner.nextInt(); // 报酬
        }

        // 调用方法计算最大报酬
        int maxReward = calculateMaxReward(T, works);

        // 输出结果
        System.out.println("在指定工作时长内工作可获得的最大报酬为: " + maxReward);

        scanner.close();
    }

    /**
     * 使用动态规划计算最大报酬
     * @param T 工作时长
     * @param works 工作数组,works[i][0]为耗时,works[i][1]为报酬
     * @return 最大报酬
     */
    public static int calculateMaxReward(int T, int[][] works) {
        // 初始化动态规划数组,dp[i]表示在工作时长为i的情况下可以获得的最大报酬
        int[] dp = new int[T + 1];

        // 遍历每个工作
        for (int[] work : works) {
            int t = work[0]; // 耗时
            int w = work[1]; // 报酬

            // 从T开始向下遍历到t,更新dp数组
            for (int j = T; j >= t; j--) {
                // 当前工作时长j下的最大报酬为当前工作的报酬与剩余工作时长(j-t)下的最大报酬之和,与之前的最大报酬进行比较并取较大值
                dp[j] = Math.max(dp[j], dp[j - t] + w);
            }
        }

        // dp[T]即为在最大工作时长T内可以获得的最大报酬
        return dp[T];
    }
}

注意事项

  • 在实现时,需要注意遍历顺序,即从大到小遍历工作时长,以避免重复选择同一工作。
  • 题目中给出的工作数量和工作时长范围较大,因此需要考虑算法的效率,动态规划是解决此类问题的有效方法。
  • 在实际机试中,输入数据会由测试系统提供,考生需要根据题目要求读取输入并输出结果。

运行解析

假如当T=10(工作时长为10小时)且n=2(有2项工作时),我们可以详细跟踪calculateMaxReward方法的运行步骤。假设给定的两项工作分别是(2, 5)(耗时2小时,报酬5)和(3, 6)(耗时3小时,报酬6)。

以下是calculateMaxReward方法的运行步骤:

  1. 初始化dp数组

    dp = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]  # 长度为T+1=11的数组,初始值都为0
    
  2. 处理第一项工作(2, 5)

    • T=10开始向下遍历到t=2(工作的耗时)。
    • j=10时,dp[10] = max(dp[10], dp[10-2] + 5) = max(0, 0 + 5) = 5
    • j=9时,dp[9] = max(dp[9], dp[9-2] + 5) = max(0, 0 + 5) = 5(注意:虽然这里dp[7]是0,但因为还没有更新dp[7]以下的值,所以仍然使用初始值0)。
    • j<9时,由于j-2会小于0,所以这些迭代实际上不会改变dp数组的值(但在这个特定例子中,我们不需要考虑它们,因为t=2)。

    此时,dp数组变为:

    dp = [0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5]
    
  3. 处理第二项工作(3, 6)

    • 再次从T=10开始向下遍历到t=3(工作的耗时)。

    • j=10时,dp[10] = max(dp[10], dp[10-3] + 6) = max(5, 5 + 6) = 11(因为dp[7]在第一步后仍然是0,但这里我们使用的是更新后的dp[7]的值,即dp[7]的“未来值”,在实际代码中,这是通过从T向下遍历来保证的)。

    • j=9时,dp[9] = max(dp[9], dp[9-3] + 6) = max(5, 2 + 6) = 8(注意:这里dp[6]实际上是上一轮迭代中未更新的值,但在实际执行时,由于我们从T开始向下遍历,所以dp[6]在到达这里之前已经被更新为dp[6]的“未来最大值”)。然而,在这个特定步骤中,我们实际上不需要考虑dp[6]的值,因为dp[9]的更新将基于dp[6]的“未来值”(如果有的话),但在这个例子中,dp[9]的更新仅依赖于dp[6]的当前值(即0,因为还没有被更新),所以实际上dp[9]将更新为max(5, 0 + 6) = 6,但由于dp[9]在第一步后已经是5,所以这里它会被更新为更大的值6(尽管在这个特定情况下,它实际上会被后续迭代覆盖为更大的值)。然而,为了清晰起见,我们保持上面的解释,但请注意实际执行中的差异。

    • j=8时,dp[8] = max(dp[8], dp[8-3] + 6) = max(0, 5 + 6) = 11(但这里dp[8]实际上在后续迭代中会被覆盖为更小的值,因为dp[5]在后续没有工作可以更新它,所以保持为0)。然而,重要的是要注意,在动态规划中,我们总是取到当前为止的最大值,所以即使dp[8]后来被覆盖,dp[10]的值已经基于当前信息被正确计算。

    • 类似地,我们可以继续计算j=7j=6(尽管在这个例子中,它们不会影响最终结果,因为dp[10]的值已经确定)。

    最终,dp数组(在考虑到所有迭代后)将类似于:

    dp = [0, 0, 0, 0, 0, 0, 6, 6, 6, 11, 11]  # 注意:这里的dp[7], dp[8], dp[9]的值是示例性的,实际值可能因实现而异
    

    但重要的是,我们关注的是dp[10]的值,它是11,这表示在10小时的工作时长内可以获得的最大报酬是11。

请注意,上面的dp数组在j=8j<8时的更新是示例性的,并且可能因实际代码实现中的遍历顺序和更新逻辑而略有不同。然而,关键点是dp[10]的值将正确地反映在给定时长和给定工作下的最大报酬。

华为OD机试真题-学生重新排队是一个典的编程问题,下面是问题和解决路: 问题描述: 有n个学生站成一排,每个学生都有一个独一无二身份ID。现在给定一个初始的学生排列顺序,以及一系列的交换操作,交换操作表示将两个学生的位置进行交换。请你编写一个算法,输出最终的学生排列顺序。 解决思路: 这个问题可以使用数组来表示学生的排列顺序。首先,我们需要根据初始的学生排列顺序构建一个映射表,将每个学生的ID与其在数组中的位置对应起来。然后,我们按照给定的交换操作,更新映射表中学生的位置信息。最后,根据更新后的映射表,构建最终的学生排列顺序。 具体步骤如下: 1. 构建映射表:遍历初始的学生排列顺序,将每个学生的ID与其在数组中的位置对应起来,可以使用哈希表来实现。 2. 执行交换操作:按照给定的交换操作,更新映射表中学生的位置信息。 3. 构建最终的学生排列顺序:根据更新后的映射表,构建最终的学生排列顺序。 下面是一个示例代码,用于解决这个问题: ```python def rearrange_students(initial_order, swap_operations): # 构建映射表 mapping = {} for i, student_id in enumerate(initial_order): mapping[student_id] = i # 执行交换操作 for swap in swap_operations: student1, student2 = swap mapping[student1], mapping[student2] = mapping[student2], mapping[student1] # 构建最终的学生排列顺序 final_order = [0] * len(initial_order) for student_id, position in mapping.items(): final_order[position] = student_id return final_order ``` 使用上述代码,你可以通过传入初始的学生排列顺序和交换操作,得到最终的学生排列顺序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值