Skip to content

Commit 66d6969

Browse files
committed
feat: Added Weighted Interval Scheduling
1 parent 9a572de commit 66d6969

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

DIRECTORY.md

+1
Original file line numberDiff line numberDiff line change
@@ -1195,6 +1195,7 @@
11951195
* [Non Preemptive Shortest Job First](scheduling/non_preemptive_shortest_job_first.py)
11961196
* [Round Robin](scheduling/round_robin.py)
11971197
* [Shortest Job First](scheduling/shortest_job_first.py)
1198+
* [Weighted Interval Scheduling](scheduling/weighted_interval_scheduling.py)
11981199

11991200
## Searches
12001201
* [Binary Search](searches/binary_search.py)
+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Implementation of Weighted Interval Scheduling algorithm
2+
# In this algorithm, we are given a list of jobs with start and end times, and each job has a specific weight.
3+
# The goal is to find the maximum weight subset of non-overlapping jobs.
4+
# https://en.wikipedia.org/wiki/Interval_scheduling#:~:text=their%20finishing%20times.-,Weighted,-%5Bedit%5D
5+
6+
from __future__ import annotations
7+
8+
9+
def latest_non_conflict(jobs: list[tuple[int, int, int]], n: int) -> int:
10+
"""
11+
This function finds the latest job that does not conflict with the current job at index `n`.
12+
The jobs are given as (start_time, end_time, weight), and the jobs should be sorted by end time.
13+
It returns the index of the latest job that finishes before the current job starts.
14+
Return: The index of the latest non-conflicting job.
15+
>>> latest_non_conflict([(1, 3, 50), (2, 5, 20), (4, 6, 30)], 2)
16+
0
17+
>>> latest_non_conflict([(1, 3, 50), (3, 4, 60), (5, 9, 70)], 2)
18+
1
19+
"""
20+
for j in range(n - 1, -1, -1):
21+
if jobs[j][1] <= jobs[n][0]:
22+
return j
23+
return -1
24+
25+
26+
def find_max_weight(jobs: list[tuple[int, int, int]]) -> int:
27+
"""
28+
This function calculates the maximum weight of non-overlapping jobs using dynamic programming.
29+
Each job is represented by a tuple (start_time, end_time, weight).
30+
The function builds a DP table where each entry `dp[i]` represents the maximum weight achievable
31+
using jobs from index 0 to i.
32+
Return: The maximum achievable weight without overlapping jobs.
33+
>>> find_max_weight([(1, 3, 50), (2, 5, 20), (4, 6, 30)])
34+
80
35+
>>> find_max_weight([(1, 3, 10), (2, 5, 100), (6, 8, 15)])
36+
115
37+
>>> find_max_weight([(1, 3, 20), (3, 5, 30), (6, 19, 60), (2, 100, 200)])
38+
200
39+
"""
40+
# Sort jobs based on their end times
41+
jobs.sort(key=lambda x: x[1])
42+
43+
# Initialize dp array to store the maximum weight up to each job
44+
n = len(jobs)
45+
dp = [0] * n
46+
dp[0] = jobs[0][2] # The weight of the first job is the initial value
47+
48+
for i in range(1, n):
49+
# Include the current job
50+
include_weight = jobs[i][2]
51+
latest_job = latest_non_conflict(jobs, i)
52+
if latest_job != -1:
53+
include_weight += dp[latest_job]
54+
55+
# Exclude the current job, and take the maximum of including or excluding
56+
dp[i] = max(include_weight, dp[i - 1])
57+
58+
return dp[-1] # The last entry contains the maximum weight
59+
60+
61+
if __name__ == "__main__":
62+
# Example list of jobs (start_time, end_time, weight)
63+
jobs = [(1, 2, 50), (3, 5, 20), (6, 19, 100), (2, 100, 200)]
64+
65+
# Ensure we have jobs to process
66+
if len(jobs) == 0:
67+
print("No jobs available to process")
68+
raise SystemExit(0)
69+
70+
# Calculate the maximum weight for non-overlapping jobs
71+
max_weight = find_max_weight(jobs)
72+
73+
# Print the result
74+
print(f"The maximum weight of non-overlapping jobs is {max_weight}")

0 commit comments

Comments
 (0)