Problem

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.

Examples

Example 1

1
2
3
4
5
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

1
2
3
4
5
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 <= 10^5
  • 1 <= offers.length <= 10^5
  • offers[i].length == 3
  • 0 <= starti <= endi <= n - 1
  • 1 <= goldi <= 10^3

Solution

Intuition

This is a classic weighted interval scheduling problem. To maximize profit, we can use dynamic programming: for each house, decide whether to take an offer ending at that house (and add its profit to the best up to its start-1), or skip it. Binary search helps efficiently find the best previous non-overlapping offer.

Approach

  1. Sort offers by their end position.
  2. Use a DP array dp[i] where dp[i] is the max profit for houses up to i.
  3. For each offer, use binary search to find the last offer that ends before the current offer’s start.
  4. For each offer, update dp[offer.end+1] = max(dp[offer.end+1], dp[offer.start] + offer.gold).
  5. The answer is the maximum value in dp.

Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution {
public:
    int maximizeTheProfit(int n, vector<vector<int>>& offers) {
        vector<vector<pair<int,int>>> endAt(n);
        for (auto& o : offers) endAt[o[1]].emplace_back(o[0], o[2]);
        vector<int> dp(n+1, 0);
        for (int i = 0; i < n; ++i) {
            dp[i+1] = max(dp[i+1], dp[i]);
            for (auto& [l, g] : endAt[i]) {
                dp[i+1] = max(dp[i+1], dp[l] + g);
            }
        }
        return dp[n];
    }
};
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
func maximizeTheProfit(n int, offers [][]int) int {
    endAt := make([][]struct{l, g int}, n)
    for _, o := range offers {
        endAt[o[1]] = append(endAt[o[1]], struct{l, g int}{o[0], o[2]})
    }
    dp := make([]int, n+1)
    for i := 0; i < n; i++ {
        if dp[i+1] < dp[i] {
            dp[i+1] = dp[i]
        }
        for _, p := range endAt[i] {
            if dp[i+1] < dp[p.l]+p.g {
                dp[i+1] = dp[p.l]+p.g
            }
        }
    }
    return dp[n]
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution {
    public int maximizeTheProfit(int n, int[][] offers) {
        List<int[]>[] endAt = new ArrayList[n];
        for (int i = 0; i < n; i++) endAt[i] = new ArrayList<>();
        for (int[] o : offers) endAt[o[1]].add(new int[]{o[0], o[2]});
        int[] dp = new int[n+1];
        for (int i = 0; i < n; i++) {
            dp[i+1] = Math.max(dp[i+1], dp[i]);
            for (int[] p : endAt[i]) {
                dp[i+1] = Math.max(dp[i+1], dp[p[0]] + p[1]);
            }
        }
        return dp[n];
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Solution {
    fun maximizeTheProfit(n: Int, offers: Array<IntArray>): Int {
        val endAt = Array(n) { mutableListOf<Pair<Int, Int>>() }
        for (o in offers) endAt[o[1]].add(o[0] to o[2])
        val dp = IntArray(n+1)
        for (i in 0 until n) {
            dp[i+1] = maxOf(dp[i+1], dp[i])
            for ((l, g) in endAt[i]) {
                dp[i+1] = maxOf(dp[i+1], dp[l] + g)
            }
        }
        return dp[n]
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Solution:
    def maximizeTheProfit(self, n: int, offers: list[list[int]]) -> int:
        end_at = [[] for _ in range(n)]
        for l, r, g in offers:
            end_at[r].append((l, g))
        dp = [0] * (n+1)
        for i in range(n):
            dp[i+1] = max(dp[i+1], dp[i])
            for l, g in end_at[i]:
                dp[i+1] = max(dp[i+1], dp[l] + g)
        return dp[n]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
impl Solution {
    pub fn maximize_the_profit(n: i32, offers: Vec<Vec<i32>>) -> i32 {
        let n = n as usize;
        let mut end_at = vec![vec![]; n];
        for o in &offers {
            end_at[o[1] as usize].push((o[0] as usize, o[2]));
        }
        let mut dp = vec![0; n+1];
        for i in 0..n {
            dp[i+1] = dp[i+1].max(dp[i]);
            for &(l, g) in &end_at[i] {
                dp[i+1] = dp[i+1].max(dp[l] + g);
            }
        }
        dp[n]
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Solution {
    maximizeTheProfit(n: number, offers: number[][]): number {
        const endAt: [number, number][][] = Array.from({length: n}, () => []);
        for (const [l, r, g] of offers) endAt[r].push([l, g]);
        const dp = new Array(n+1).fill(0);
        for (let i = 0; i < n; i++) {
            dp[i+1] = Math.max(dp[i+1], dp[i]);
            for (const [l, g] of endAt[i]) {
                dp[i+1] = Math.max(dp[i+1], dp[l] + g);
            }
        }
        return dp[n];
    }
}

Complexity

  • ⏰ Time complexity: O(n + m) where n is the number of houses and m is the number of offers (since each offer is processed once, and each house is visited once).
  • 🧺 Space complexity: O(n + m) — for the DP array and offer storage.