Skip to content

Commit fd4741d

Browse files
committed
feat: add maximum_profit_in_job_scheduling
1 parent 4ba5bdd commit fd4741d

File tree

12 files changed

+414
-15
lines changed

12 files changed

+414
-15
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"problem_name": "maximum_profit_in_job_scheduling",
3+
"solution_class_name": "Solution",
4+
"problem_number": "1235",
5+
"problem_title": "Maximum Profit in Job Scheduling",
6+
"difficulty": "Hard",
7+
"topics": "Array, Binary Search, Dynamic Programming, Sorting",
8+
"tags": ["grind-75"],
9+
"readme_description": "We have `n` jobs, where every job is scheduled to be done from `startTime[i]` to `endTime[i]`, obtaining a profit of `profit[i]`.\n\nYou're given the `startTime`, `endTime` and `profit` arrays, return the maximum profit you can take such that there are no two jobs in the subset with overlapping time range.\n\nIf you choose a job that ends at time `X` you will be able to start another job that starts at time `X`.",
10+
"readme_examples": [
11+
{
12+
"content": "![Example 1](https://assets.leetcode.com/uploads/2019/10/10/sample1_1584.png)\n\n```\nInput: startTime = [1,2,3,3], endTime = [3,4,5,6], profit = [50,10,40,70]\nOutput: 120\n```\n**Explanation:** The subset chosen is the first and fourth job. Time range [1-3]+[3-6] , we get profit of 120 = 50 + 70."
13+
},
14+
{
15+
"content": "![Example 2](https://assets.leetcode.com/uploads/2019/10/10/sample22_1584.png)\n\n```\nInput: startTime = [1,2,3,4,6], endTime = [3,5,10,6,9], profit = [20,20,100,70,60]\nOutput: 150\n```\n**Explanation:** The subset chosen is the first, fourth and fifth job. Profit obtained 150 = 20 + 70 + 60."
16+
},
17+
{
18+
"content": "![Example 3](https://assets.leetcode.com/uploads/2019/10/10/sample3_1584.png)\n\n```\nInput: startTime = [1,1,1], endTime = [2,3,4], profit = [5,6,4]\nOutput: 6\n```"
19+
}
20+
],
21+
"readme_constraints": "- `1 <= startTime.length == endTime.length == profit.length <= 5 * 10^4`\n- `1 <= startTime[i] < endTime[i] <= 10^9`\n- `1 <= profit[i] <= 10^4`",
22+
"readme_additional": "",
23+
"solution_imports": "",
24+
"solution_methods": [
25+
{
26+
"name": "job_scheduling",
27+
"parameters": "start_time: list[int], end_time: list[int], profit: list[int]",
28+
"return_type": "int",
29+
"dummy_return": "0"
30+
}
31+
],
32+
"test_imports": "import pytest\nfrom leetcode_py.test_utils import logged_test\nfrom .solution import Solution",
33+
"test_class_name": "MaximumProfitInJobScheduling",
34+
"test_helper_methods": [
35+
{ "name": "setup_method", "parameters": "", "body": "self.solution = Solution()" }
36+
],
37+
"test_methods": [
38+
{
39+
"name": "test_job_scheduling",
40+
"parametrize": "start_time, end_time, profit, expected",
41+
"parametrize_typed": "start_time: list[int], end_time: list[int], profit: list[int], expected: int",
42+
"test_cases": "[([1, 2, 3, 3], [3, 4, 5, 6], [50, 10, 40, 70], 120), ([1, 2, 3, 4, 6], [3, 5, 10, 6, 9], [20, 20, 100, 70, 60], 150), ([1, 1, 1], [2, 3, 4], [5, 6, 4], 6)]",
43+
"body": "result = self.solution.job_scheduling(start_time, end_time, profit)\nassert result == expected"
44+
}
45+
],
46+
"playground_imports": "from solution import Solution",
47+
"playground_test_case": "# Example test case\nstart_time = [1, 2, 3, 3]\nend_time = [3, 4, 5, 6]\nprofit = [50, 10, 40, 70]\nexpected = 120",
48+
"playground_execution": "result = Solution().job_scheduling(start_time, end_time, profit)\nresult",
49+
"playground_assertion": "assert result == expected"
50+
}

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PYTHON_VERSION = 3.13
2-
PROBLEM ?= trapping_rain_water
2+
PROBLEM ?= maximum_profit_in_job_scheduling
33
FORCE ?= 0
44
COMMA := ,
55

leetcode/k_closest_points_to_origin/playground.ipynb

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,43 @@
66
"id": "imports",
77
"metadata": {},
88
"outputs": [],
9-
"source": ["from solution import Solution"]
9+
"source": [
10+
"from solution import Solution"
11+
]
1012
},
1113
{
1214
"cell_type": "code",
1315
"execution_count": null,
1416
"id": "setup",
1517
"metadata": {},
1618
"outputs": [],
17-
"source": ["# Example test case\npoints = [[1, 3], [-2, 2]]\nk = 1\nexpected = [[-2, 2]]"]
19+
"source": [
20+
"# Example test case\n",
21+
"points = [[1, 3], [-2, 2]]\n",
22+
"k = 1\n",
23+
"expected = [[-2, 2]]"
24+
]
1825
},
1926
{
2027
"cell_type": "code",
2128
"execution_count": null,
2229
"id": "execute",
2330
"metadata": {},
2431
"outputs": [],
25-
"source": ["result = Solution().k_closest(points, k)\nresult"]
32+
"source": [
33+
"result = Solution().k_closest(points, k)\n",
34+
"result"
35+
]
2636
},
2737
{
2838
"cell_type": "code",
2939
"execution_count": null,
3040
"id": "test",
3141
"metadata": {},
3242
"outputs": [],
33-
"source": ["assert sorted(result) == sorted(expected)"]
43+
"source": [
44+
"assert sorted(result) == sorted(expected)"
45+
]
3446
}
3547
],
3648
"metadata": {

leetcode/longest_palindrome/playground.ipynb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"metadata": {},
3030
"outputs": [],
3131
"source": [
32-
"result = Solution().longest_palindrome(s)\nresult"
32+
"result = Solution().longest_palindrome(s)\n",
33+
"result"
3334
]
3435
},
3536
{
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Maximum Profit in Job Scheduling
2+
3+
**Difficulty:** Hard
4+
**Topics:** Array, Binary Search, Dynamic Programming, Sorting
5+
**Tags:** grind-75
6+
7+
**LeetCode:** [Problem 1235](https://leetcode.com/problems/maximum-profit-in-job-scheduling/description/)
8+
9+
## Problem Description
10+
11+
We have `n` jobs, where every job is scheduled to be done from `startTime[i]` to `endTime[i]`, obtaining a profit of `profit[i]`.
12+
13+
You're given the `startTime`, `endTime` and `profit` arrays, return the maximum profit you can take such that there are no two jobs in the subset with overlapping time range.
14+
15+
If you choose a job that ends at time `X` you will be able to start another job that starts at time `X`.
16+
17+
## Examples
18+
19+
### Example 1:
20+
21+
![Example 1](https://assets.leetcode.com/uploads/2019/10/10/sample1_1584.png)
22+
23+
```
24+
Input: startTime = [1,2,3,3], endTime = [3,4,5,6], profit = [50,10,40,70]
25+
Output: 120
26+
```
27+
28+
**Explanation:** The subset chosen is the first and fourth job. Time range [1-3]+[3-6] , we get profit of 120 = 50 + 70.
29+
30+
### Example 2:
31+
32+
![Example 2](https://assets.leetcode.com/uploads/2019/10/10/sample22_1584.png)
33+
34+
```
35+
Input: startTime = [1,2,3,4,6], endTime = [3,5,10,6,9], profit = [20,20,100,70,60]
36+
Output: 150
37+
```
38+
39+
**Explanation:** The subset chosen is the first, fourth and fifth job. Profit obtained 150 = 20 + 70 + 60.
40+
41+
### Example 3:
42+
43+
![Example 3](https://assets.leetcode.com/uploads/2019/10/10/sample3_1584.png)
44+
45+
```
46+
Input: startTime = [1,1,1], endTime = [2,3,4], profit = [5,6,4]
47+
Output: 6
48+
```
49+
50+
## Constraints
51+
52+
- `1 <= startTime.length == endTime.length == profit.length <= 5 * 10^4`
53+
- `1 <= startTime[i] < endTime[i] <= 10^9`
54+
- `1 <= profit[i] <= 10^4`

leetcode/maximum_profit_in_job_scheduling/__init__.py

Whitespace-only changes.
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 1,
6+
"id": "imports",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"import bisect\n",
11+
"\n",
12+
"from solution import Solution"
13+
]
14+
},
15+
{
16+
"cell_type": "code",
17+
"execution_count": 2,
18+
"id": "setup",
19+
"metadata": {},
20+
"outputs": [],
21+
"source": [
22+
"# Example test case\n",
23+
"start_time = [1, 2, 3, 3]\n",
24+
"end_time = [3, 4, 5, 6]\n",
25+
"profit = [50, 10, 40, 70]\n",
26+
"expected = 120"
27+
]
28+
},
29+
{
30+
"cell_type": "code",
31+
"execution_count": 3,
32+
"id": "execute",
33+
"metadata": {},
34+
"outputs": [
35+
{
36+
"data": {
37+
"text/plain": [
38+
"120"
39+
]
40+
},
41+
"execution_count": 3,
42+
"metadata": {},
43+
"output_type": "execute_result"
44+
}
45+
],
46+
"source": [
47+
"result = Solution().job_scheduling(start_time, end_time, profit)\n",
48+
"result"
49+
]
50+
},
51+
{
52+
"cell_type": "code",
53+
"execution_count": 4,
54+
"id": "test",
55+
"metadata": {},
56+
"outputs": [],
57+
"source": [
58+
"assert result == expected"
59+
]
60+
},
61+
{
62+
"cell_type": "markdown",
63+
"id": "dd4ebe51",
64+
"metadata": {},
65+
"source": [
66+
"# Bisect"
67+
]
68+
},
69+
{
70+
"cell_type": "code",
71+
"execution_count": 5,
72+
"id": "3596ff83",
73+
"metadata": {},
74+
"outputs": [
75+
{
76+
"name": "stdout",
77+
"output_type": "stream",
78+
"text": [
79+
"Original array: [1, 3, 3, 5, 7]\n",
80+
"Indices: [0, 1, 2, 3, 4]\n",
81+
"\n"
82+
]
83+
}
84+
],
85+
"source": [
86+
"# Demo array\n",
87+
"arr = [1, 3, 3, 5, 7]\n",
88+
"print(f\"Original array: {arr}\")\n",
89+
"print(f\"Indices: {list(range(len(arr)))}\")\n",
90+
"print()"
91+
]
92+
},
93+
{
94+
"cell_type": "code",
95+
"execution_count": 6,
96+
"id": "508a52e9",
97+
"metadata": {},
98+
"outputs": [
99+
{
100+
"name": "stdout",
101+
"output_type": "stream",
102+
"text": [
103+
"=== bisect_left vs bisect_right ===\n",
104+
"x=0: left=0, right=0\n",
105+
"x=3: left=1, right=3\n",
106+
"x=4: left=3, right=3\n",
107+
"x=8: left=5, right=5\n",
108+
"\n"
109+
]
110+
}
111+
],
112+
"source": [
113+
"# bisect_left vs bisect_right\n",
114+
"print(\"=== bisect_left vs bisect_right ===\")\n",
115+
"for x in [0, 3, 4, 8]:\n",
116+
" left = bisect.bisect_left(arr, x)\n",
117+
" right = bisect.bisect_right(arr, x)\n",
118+
" print(f\"x={x}: left={left}, right={right}\")\n",
119+
"print()"
120+
]
121+
},
122+
{
123+
"cell_type": "code",
124+
"execution_count": 7,
125+
"id": "b5dfa28a",
126+
"metadata": {},
127+
"outputs": [
128+
{
129+
"name": "stdout",
130+
"output_type": "stream",
131+
"text": [
132+
"=== insort demonstration ===\n",
133+
"Before: [1, 3, 5]\n",
134+
"After insort(4): [1, 3, 4, 5]\n",
135+
"After insort(3): [1, 3, 3, 4, 5]\n",
136+
"\n"
137+
]
138+
}
139+
],
140+
"source": [
141+
"# insort demonstration\n",
142+
"print(\"=== insort demonstration ===\")\n",
143+
"test_arr = [1, 3, 5]\n",
144+
"print(f\"Before: {test_arr}\")\n",
145+
"bisect.insort(test_arr, 4)\n",
146+
"print(f\"After insort(4): {test_arr}\")\n",
147+
"bisect.insort(test_arr, 3)\n",
148+
"print(f\"After insort(3): {test_arr}\")\n",
149+
"print()"
150+
]
151+
}
152+
],
153+
"metadata": {
154+
"kernelspec": {
155+
"display_name": "leetcode-py-py3.13",
156+
"language": "python",
157+
"name": "python3"
158+
},
159+
"language_info": {
160+
"codemirror_mode": {
161+
"name": "ipython",
162+
"version": 3
163+
},
164+
"file_extension": ".py",
165+
"mimetype": "text/x-python",
166+
"name": "python",
167+
"nbconvert_exporter": "python",
168+
"pygments_lexer": "ipython3",
169+
"version": "3.13.7"
170+
}
171+
},
172+
"nbformat": 4,
173+
"nbformat_minor": 5
174+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import bisect
2+
3+
4+
class Solution:
5+
# Time: O(n log n)
6+
# Space: O(n)
7+
def job_scheduling(self, start_time: list[int], end_time: list[int], profit: list[int]) -> int:
8+
9+
jobs = sorted(zip(end_time, start_time, profit))
10+
dp = [0] * len(jobs)
11+
12+
for i, (end, start, p) in enumerate(jobs):
13+
# Binary search for latest non-overlapping job
14+
j = bisect.bisect_right([job[0] for job in jobs[:i]], start) - 1
15+
16+
# Take current job + best profit from non-overlapping jobs
17+
take = p + (dp[j] if j >= 0 else 0)
18+
# Skip current job
19+
skip = dp[i - 1] if i > 0 else 0
20+
21+
dp[i] = max(take, skip)
22+
23+
return dp[-1] if jobs else 0
24+
25+
26+
# bisect and insort Explanation:
27+
#
28+
# Etymology: "bisect" = bi (two) + sect (cut) = cut into two parts
29+
# Bisection method = binary search algorithm that repeatedly cuts search space in half
30+
#
31+
# bisect module provides binary search for SORTED lists (O(log n)):
32+
# - bisect_left(arr, x): leftmost insertion position
33+
# - bisect_right(arr, x): rightmost insertion position (default)
34+
# - bisect(arr, x): alias for bisect_right
35+
#
36+
# insort module maintains sorted order while inserting:
37+
# - insort_left(arr, x): insert at leftmost position
38+
# - insort_right(arr, x): insert at rightmost position (default)
39+
# - insort(arr, x): alias for insort_right
40+
#
41+
# Examples:
42+
# arr = [1, 3, 3, 5]
43+
# bisect_left(arr, 3) → 1 (before existing 3s)
44+
# bisect_right(arr, 3) → 3 (after existing 3s)
45+
# bisect_right(arr, 4) → 3 (between 3 and 5)
46+
#
47+
# insort(arr, 4) → arr becomes [1, 3, 3, 4, 5]
48+
#
49+
# In our solution:
50+
# bisect_right([2,4,6], 5) = 2 (insertion position)
51+
# j = 2 - 1 = 1 (index of latest job ending ≤ start_time)

0 commit comments

Comments
 (0)