leetcode
leetcode 2401 ~ 2450
销售利润最大化

销售利润最大化

难度:

标签:

题目描述

You are given an integer n representing the number of houses on a number line, numbered from 0 to n - 1.

Additionally, you are given a 2D integer array offers where offers[i] = [starti, endi, goldi], indicating that ith buyer wants to buy all the houses from starti to endi for goldi amount of gold.

As a salesman, your goal is to maximize your earnings by strategically selecting and selling houses to buyers.

Return the maximum amount of gold you can earn.

Note that different buyers can't buy the same house, and some houses may remain unsold.

 

Example 1:

Input: n = 5, offers = [[0,0,1],[0,2,2],[1,3,2]]
Output: 3
Explanation: There are 5 houses numbered from 0 to 4 and there are 3 purchase offers.
We sell houses in the range [0,0] to 1st buyer for 1 gold and houses in the range [1,3] to 3rd buyer for 2 golds.
It can be proven that 3 is the maximum amount of gold we can achieve.

Example 2:

Input: n = 5, offers = [[0,0,1],[0,2,10],[1,3,2]]
Output: 10
Explanation: There are 5 houses numbered from 0 to 4 and there are 3 purchase offers.
We sell houses in the range [0,2] to 2nd buyer for 10 golds.
It can be proven that 10 is the maximum amount of gold we can achieve.

 

Constraints:

  • 1 <= n <= 105
  • 1 <= offers.length <= 105
  • offers[i].length == 3
  • 0 <= starti <= endi <= n - 1
  • 1 <= goldi <= 103

代码结果

运行时间: 203 ms, 内存: 40.9 MB


/*
 * 思路:
 * 1. 使用Java Stream API对offers进行排序。
 * 2. 使用数组dp存储动态规划的结果。
 * 3. 使用Stream进行处理。
 */
import java.util.Arrays;

public class MaxGoldStream {
    public int maximizeGold(int n, int[][] offers) {
        Arrays.sort(offers, (a, b) -> Integer.compare(a[1], b[1])); // 按结束时间排序
        int[] dp = new int[n + 1];
        Arrays.stream(offers).forEach(offer -> {
            int start = offer[0], end = offer[1], gold = offer[2];
            dp[end + 1] = Math.max(dp[end + 1], dp[start] + gold);
            dp[end + 1] = Math.max(dp[end + 1], dp[end]);
        });
        return dp[n];
    }
}

解释

方法:

本题的解法采用了动态规划的思路。定义 f[i] 为考虑到第 i-1 所房屋时能获得的最大收益。对于每个结尾为 i 的要约,我们在动态规划数组中更新 f[i+1] 为 max(f[i+1], f[start] + gold),其中 start 和 gold 是当前要约的起始房屋和提供的金币数。这样,我们不断更新在每个位置可能达到的最大收益,考虑的是接受当前要约与不接受当前要约的情况。最终,f[n] 存储的是考虑整个房屋数组时的最大收益。

时间复杂度:

O(n + m)

空间复杂度:

O(n + m)

代码细节讲解

🦆
动态规划数组 f[i] 是如何初始化的,为什么初始值设置为0?
动态规划数组 f[i] 初始化为0是因为在没有任何要约的情况下,任何房屋的最大收益应为0。这个初始化也确保了算法的正确性,因为它代表着从0个房屋开始,没有任何收益的基础状态。数组 f[i] 中的每个值都代表了考虑到第 i-1 个房屋时的最大收益,因此 f[0] 表示没有房屋可考虑时的收益,自然是0。
🦆
在结束字典 end_dict 中处理要约时,为什么选择按照结束编号 end 来分组要约?
在字典 end_dict 中按照结束编号 end 来分组要约,是为了方便在动态规划过程中快速查找所有在特定房屋编号结束的要约。这样可以直接在更新 f[i+1] 的值时,检查所有在房屋 i 结束的要约,从而决定是否接受这些要约以及如何更新收益。这种分组方式提高了算法的效率,使得每次更新只需考虑相关的要约,而不必每次都遍历所有要约。
🦆
对于没有任何要约结束的房屋编号 i,动态规划中的 f[i+1] 如何更新?
如果对于某个房屋编号 i 没有任何要约结束,那么在动态规划中,f[i+1] 将简单地被更新为 f[i] 的值。这表示在房屋编号 i 处没有接受新的要约,因此最大收益保持与前一个房屋 i-1 相同。这样的更新确保了如果没有更好的选择(即没有新的要约或新的要约不如当前收益),我们保持现有的最大收益。
🦆
在动态规划的实现中,如果存在多个要约同时结束在同一房屋 i,这些要约是如何互相影响最终的 f[i+1] 值的?
如果存在多个要约同时结束在同一房屋 i,动态规划中的实现会检查每一个要约,并尝试更新 f[i+1] 的值。具体来说,对于每个要约,我们会比较接受该要约的收益(即 f[要约的起始房屋] 加上该要约提供的金币数)与当前 f[i+1] 的值,选择两者中的最大值作为新的 f[i+1]。这意味着 f[i+1] 的最终值将是所有可能要约组合中可能获得的最大收益。

相关问题