最大报酬

题目描述:

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

输入描述

输入的第一行为两个正整数 Tn

T 代表工作时长 (单位 h,0<T< 1000000),

n 代表工作数量 (1<n<= 3000)。

接下来是 n 行,每行包含两个整数 tw

t 代表该工作消耗的时长(单位 h,t>0),w 代表该项工作的报酬

输出描述

输出小明指定工作时长内工作可获得的最大报酬

示例1

输入

40 3
20 10
20 20
20 5

输出

30

题目解析

这道题目本质上是一个 0-1 背包问题。每项工作可以看作是一个物品,工作时长就是物品的重量,报酬就是物品的价值。我们需要在有限的时间(背包容量)内,选择一些工作(物品)来获得最大的报酬(价值)。

解决这个问题的关键在于使用动态规划。定义一个二维数组 dp[i][j],表示前 i 个工作在 j 小时内能获得的最大报酬。

状态转移方程如下:

dp[i][j] = max(dp[i-1][j], dp[i-1][j-t[i]] + w[i])  if j >= t[i]
dp[i][j] = dp[i-1][j]                               if j < t[i]

这个方程的含义是:对于第 i 个工作,有两种选择:做或不做。如果不做,那么最大报酬就是前 i-1 个工作在 j 小时内的最大报酬。如果做,那么需要先腾出这个工作所需的时间,然后加上这个工作的报酬。

  • dp[i-1][j] 表示不选择第 i 个任务时的最大报酬。
  • dp[i-1][j- job.t] + job.w 表示选择第 i 个任务时的最大报酬(前提是 j >= job.t 即给定的时间足以完成该任务)。

我们需要初始化 dp[0][j] = 0,因为在没有任务时,任何时间的报酬都是 0。

进阶

由于 dp[i][j] 仅依赖于上一行 dp[i-1][...],我们可以将 dp 数组优化为一维,减少空间复杂度。

新状态转移方程为:

dp[j] = max(dp[j], dp[j-t[i]] + w[i])

注意,更新 dp 数组时,需要从后向前遍历,以确保在计算当前任务的状态时不会覆盖掉上一任务的状态。

源码 java

使用二维数组求解

// 使用二维数组求解
public class WorkReward2 {
	static Input input;
	static {
		input = new Input("40 3\n" +
				"20 10\n" +
				"20 20\n" +
				"20 5");
	}

	public static void main(String[] args) {
		String[] sts = input.nextLine().split(" ");
		int T = Integer.parseInt(sts[0]);
		int n = Integer.parseInt(sts[1]);

		// 做 i  份工作, 在 T 小时内可以获得的最大收益
		int[][] income = new int[n+1][T+1] ;

		for (int i = 1; i <= n; i++) {
			Job job = new Job(input.nextLine().split(" "));
			for (int j = 1; j <= T; j++) {
				if (j < job.t) {
					// 如果时间不足以完成该项工作,则此时的最大收益为 再吃 j 时间内完成 i - 1 件工作锁获取的收益
					income[i][j] = income[i - 1][j];
				} else {
					// 如果时间足够完成该项工作, 则最大收益为:做这个工作和不作这个工作之间 收益的最大值
					int doIt = income[i - 1][j - job.t] + job.w;
					int notDo = income[i - 1][j];
					income[i][j] = Math.max(doIt, notDo);
				}
			}
		}

		System.out.println(income[n][T]);
	}

	static class Job{
		public int t ;
		public int w;
		public Job(String[] s) {
			this.t = Integer.parseInt(s[0]);
			this.w = Integer.parseInt(s[1]);
		}
	}
}

使用一维数组求解

public class WorkReward {
	static Input input;
	static {
		input = new Input("40 3\n" +
				"20 10\n" +
				"20 20\n" +
				"20 5");
	}

	public static void main(String[] args) {
		String str = input.nextLine();
		int T = Integer.parseInt(str.split(" ")[0]);
		int n = Integer.parseInt(str.split(" ")[1]);

		int[] income = new int[T+1];

		for (int i = 0; i < n; i++) {
			Job job = new Job(input.nextLine().split(" "));
			for (int j = T; j >= job.t; j-- ){
				// 选择这份工作 总收益 = 干这份工作取得的收益  job.w + 减去这份工作需要时间后剩余的时间 可以获得的最大的收益 incone[j - job.t]
				// 不选择这份工作
				income[j] = Math.max(income[j], income[j - job.t] + job.w);
			}
		}
		System.out.println(income[T]);
	}

	static class Job{
		public int t ;
		public int w;
		public Job(String[] s) {
			this.t = Integer.parseInt(s[0]);
			this.w = Integer.parseInt(s[1]);
		}
	}
}
#OD面经#
机试 算法 文章被收录于专栏

持续更新相关算法,希望可以帮助到大家,如果大家有任何的意见或者建议,都可以留言,我能帮助解决的一定尽力去解决 希望下一个offer 就是你的

全部评论

相关推荐

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