From 66d696903f58868db95de6191ba7c089146c82a8 Mon Sep 17 00:00:00 2001 From: B Karthik Date: Sat, 5 Oct 2024 00:41:19 +0530 Subject: [PATCH] feat: Added Weighted Interval Scheduling --- DIRECTORY.md | 1 + scheduling/weighted_interval_scheduling.py | 74 ++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 scheduling/weighted_interval_scheduling.py diff --git a/DIRECTORY.md b/DIRECTORY.md index cdbbac684..dfb87d324 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -1195,6 +1195,7 @@ * [Non Preemptive Shortest Job First](scheduling/non_preemptive_shortest_job_first.py) * [Round Robin](scheduling/round_robin.py) * [Shortest Job First](scheduling/shortest_job_first.py) + * [Weighted Interval Scheduling](scheduling/weighted_interval_scheduling.py) ## Searches * [Binary Search](searches/binary_search.py) diff --git a/scheduling/weighted_interval_scheduling.py b/scheduling/weighted_interval_scheduling.py new file mode 100644 index 000000000..d58e4364d --- /dev/null +++ b/scheduling/weighted_interval_scheduling.py @@ -0,0 +1,74 @@ +# Implementation of Weighted Interval Scheduling algorithm +# In this algorithm, we are given a list of jobs with start and end times, and each job has a specific weight. +# The goal is to find the maximum weight subset of non-overlapping jobs. +# https://en.wikipedia.org/wiki/Interval_scheduling#:~:text=their%20finishing%20times.-,Weighted,-%5Bedit%5D + +from __future__ import annotations + + +def latest_non_conflict(jobs: list[tuple[int, int, int]], n: int) -> int: + """ + This function finds the latest job that does not conflict with the current job at index `n`. + The jobs are given as (start_time, end_time, weight), and the jobs should be sorted by end time. + It returns the index of the latest job that finishes before the current job starts. + Return: The index of the latest non-conflicting job. + >>> latest_non_conflict([(1, 3, 50), (2, 5, 20), (4, 6, 30)], 2) + 0 + >>> latest_non_conflict([(1, 3, 50), (3, 4, 60), (5, 9, 70)], 2) + 1 + """ + for j in range(n - 1, -1, -1): + if jobs[j][1] <= jobs[n][0]: + return j + return -1 + + +def find_max_weight(jobs: list[tuple[int, int, int]]) -> int: + """ + This function calculates the maximum weight of non-overlapping jobs using dynamic programming. + Each job is represented by a tuple (start_time, end_time, weight). + The function builds a DP table where each entry `dp[i]` represents the maximum weight achievable + using jobs from index 0 to i. + Return: The maximum achievable weight without overlapping jobs. + >>> find_max_weight([(1, 3, 50), (2, 5, 20), (4, 6, 30)]) + 80 + >>> find_max_weight([(1, 3, 10), (2, 5, 100), (6, 8, 15)]) + 115 + >>> find_max_weight([(1, 3, 20), (3, 5, 30), (6, 19, 60), (2, 100, 200)]) + 200 + """ + # Sort jobs based on their end times + jobs.sort(key=lambda x: x[1]) + + # Initialize dp array to store the maximum weight up to each job + n = len(jobs) + dp = [0] * n + dp[0] = jobs[0][2] # The weight of the first job is the initial value + + for i in range(1, n): + # Include the current job + include_weight = jobs[i][2] + latest_job = latest_non_conflict(jobs, i) + if latest_job != -1: + include_weight += dp[latest_job] + + # Exclude the current job, and take the maximum of including or excluding + dp[i] = max(include_weight, dp[i - 1]) + + return dp[-1] # The last entry contains the maximum weight + + +if __name__ == "__main__": + # Example list of jobs (start_time, end_time, weight) + jobs = [(1, 2, 50), (3, 5, 20), (6, 19, 100), (2, 100, 200)] + + # Ensure we have jobs to process + if len(jobs) == 0: + print("No jobs available to process") + raise SystemExit(0) + + # Calculate the maximum weight for non-overlapping jobs + max_weight = find_max_weight(jobs) + + # Print the result + print(f"The maximum weight of non-overlapping jobs is {max_weight}")