For working professionals
For fresh graduates
More
A Comprehensive Guide on Softw…
1. Introduction
2. 2D Transformation In CSS
3. Informatica tutorial
4. Iterator Design Pattern
5. OpenCV Tutorial
6. PyTorch
7. Activity Diagram in UML
8. Activity selection problem
Now Reading
9. AI Tutorial
10. Airflow Tutorial
11. Android Studio
12. Android Tutorial
13. Animation CSS
14. Apache Kafka Tutorial
15. Apache Spark Tutorial
16. Apex Tutorial
17. App Tutorial
18. Appium Tutorial
19. Application Layer
20. Architecture of Data Warehouse
21. Armstrong Number
22. ASP Full Form
23. AutoCAD Tutorial
24. AWS Instance Types
25. Backend Technologies
26. Bash Scripting Tutorial
27. Belady's Anomaly
28. BGP Border Gateway Protocol
29. Binary Subtraction
30. Bipartite Graph
31. Bootstrap 5 tutorial
32. Box sizing in CSS
33. Bridge vs. Repeater
34. Builder Design Pattern
35. Button CSS
36. Change Font Color Using CSS
37. Circuit Switching and Packet Switching
38. Clustered and Non-clustered Index
39. Cobol Tutorial
40. CodeIgniter Tutorial
41. Compiler Design Tutorial
42. Complete Binary Trees
43. Components of IoT
44. Computer Network Tutorial
45. Convert Octal to Binary
46. CSS Border
47. CSS Colors
48. CSS Flexbox
49. CSS Float
50. CSS Font Properties
51. CSS Full Form
52. CSS Gradient
53. CSS Margin
54. CSS nth Child
55. CSS Syntax
56. CSS Tables
57. CSS Tricks
58. CSS Variables
59. Cucumber Tutorial
60. Cyclic Redundancy Check
61. Dart Tutorial
62. Data Structures and Algorithms (DSA)
63. DCL
64. Decision Tree Algorithm
65. DES Algorithm
66. Difference Between DDL and DML
67. Difference between Encapsulation and Abstraction
68. Difference Between GET and POST
69. Difference Between Hub and Switch
70. Difference Between IPv4 and IPv6
71. Difference Between Microprocessor And Microcontroller
72. Difference between PERT and CPM
73. Difference Between Primary Key and Foreign Key
74. Difference Between Process and Thread in Java
75. Difference between RAM and ROM
76. SRAM vs. DRAM: Understanding the Difference
77. Difference Between Structure and Union
78. Difference between TCP and UDP
79. Difference between Transport Layer and Network Layer
80. Disk Scheduling Algorithms
81. Display Property in CSS
82. Domain Name System
83. Dot Net Tutorial
84. ElasticSearch Tutorial
85. Entity Framework Tutorial
86. ES6 Tutorial
87. Factory Design Pattern in Java
88. File Transfer Protocol
89. Firebase Tutorial
90. First Come First Serve
91. Flutter Basics
92. Flutter Tutorial
93. Font Family in CSS
94. Go Language Tutorial
95. Golang Tutorial
96. Graphql Tutorial
97. Half Adder and Full Adder
98. Height of Binary Tree
99. Hibernate Tutorial
100. Hive Tutorial
101. How To Become A Data Scientist
102. How to Install Anaconda Navigator
103. Install Bootstrap
104. Google Colab - How to use Google Colab
105. Hypertext Transfer Protocol
106. Infix to Postfix Conversion
107. Install SASS
108. Internet Control Message Protocol (ICMP)
109. IPv 4 address
110. JCL Programming
111. JQ Tutorial
112. JSON Tutorial
113. JSP Tutorial
114. Junit Tutorial
115. Kadanes Algorithm
116. Kafka Tutorial
117. Knapsack Problem
118. Kth Smallest Element
119. Laravel Tutorial
120. Left view of binary tree
121. Level Order Traversal
122. Linear Gradient CSS
123. Link State Routing Algorithm
124. Longest Palindromic Subsequence
125. LRU Cache Implementation
126. Matrix Chain Multiplication
127. Maximum Product Subarray
128. Median of Two Sorted Arrays
129. Memory Hierarchy
130. Merge Two Sorted Arrays
131. Microservices Tutorial
132. Missing Number in Array
133. Mockito tutorial
134. Modem vs Router
135. Mulesoft Tutorial
136. Network Devices
137. Network Devices in Computer Networks
138. Next JS Tutorial
139. Nginx Tutorial
140. Object-Oriented Programming (OOP)
141. Octal to Decimal
142. OLAP Operations
143. Opacity CSS
144. OSI Model
145. CSS Overflow
146. Padding in CSS
147. Perimeter of A Rectangle
148. Perl scripting
149. Phases of Compiler
150. Placeholder CSS
151. Position Property in CSS
152. Postfix evaluation in C
153. Powershell Tutorial
154. Primary Key vs Unique Key
155. Program To Find Area Of Triangle
156. Pseudo-Classes in CSS
157. Pseudo elements in CSS
158. Pyspark Tutorial
159. Pythagorean Triplet in an Array
160. Python Tkinter Tutorial
161. Quality of Service
162. R Language Tutorial
163. R Programming Tutorial
164. RabbitMQ Tutorial
165. Redis Tutorial
166. Redux in React
167. Regex Tutorial
168. Relation Between Transport Layer And Network Layer
169. Array Rotation in Java
170. Routing Protocols
171. Ruby On Rails
172. Ruby tutorial
173. Scala Tutorial
174. Scatter Plot Matplotlib
175. Shadow CSS
176. Shell Scripting Tutorial
177. Singleton Design Pattern
178. Snowflake Tutorial
179. Socket Programming
180. Solidity Tutorial
181. SonarQube in Java
182. Spark Tutorial
183. Spiral Model In Software Engineering
184. Splunk Tutorial for Beginners
185. Structural Design Pattern
186. Subnetting in Computer Networks
187. Sum of N Natural Numbers
188. Swift Programming Tutorial
189. TCP 3 Way Handshake
190. TensorFlow Tutorial
191. Threaded Binary Tree
192. Top View Of Binary Tree
193. Transmission Control Protocol
194. Transport Layer Protocols
195. Traversal of Binary Tree
196. Types of Queue
197. TypeScript Tutorial
198. UDP Protocol
199. Ultrasonic Sensor Arduino Code
200. Unix Tutorial for Beginners
201. V Model in Software Engineering
202. Verilog Tutorial
203. Virtualization in Cloud Computing
204. Void Pointer
205. Vue JS Tutorial
206. Weak Entity Set
207. What is Bandwidth?
208. What is Big Data
209. Checksum
210. What is Design Pattern?
211. What is Ethernet
212. What is Link State Routing
213. What Is Port In Networking
214. What is ROM?
215. Page Fault in Operating Systems
216. WPF Tutorial
217. Wireshark Tutorial
218. XML Tutorial
Welcome to the fascinating world of algorithmic problem-solving! In this comprehensive guide, we will explore the Activity Selection Problem, a classic optimization challenge frequently encountered in various scheduling and resource allocation tasks. Our journey will take us through the application of Greedy Algorithms techniques to solve this problem efficiently and effectively. Let’s dive straight into this intriguing topic!
The Activity Selection Problem is a well-known optimization problem that involves selecting a maximum number of non-conflicting activities from a given set. Each activity has a start time and an end time, and the objective is to maximize the number of activities performed within the given time frame. We will explore two powerful techniques for solving this problem: Greedy Algorithms and Dynamic Programming.
Standard Greedy Algorithms:
Greedy Algorithms are widely used for optimization problems, including the Activity Selection Problem. Let's briefly explore some standard Greedy Algorithms to build a foundation for solving our target problem:
1. Kruskal's Minimum Spanning Tree (MST):
Kruskal's algorithm finds the minimum spanning tree in a weighted graph. Though not directly related to the Activity Selection Problem, understanding MST helps grasp the principles of Greedy Algorithms.
2. Prim's Minimum Spanning Tree:
Prim's algorithm also finds the minimum spanning tree, emphasizing a different approach. While not directly tied to activity selection, it reinforces the concept of greedy choices.
3. Dijkstra's Shortest Path:
Dijkstra's algorithm determines the shortest path from a source node to all other nodes in a weighted graph. While unrelated to the Activity Selection Problem, it illustrates the application of greedy principles.
4. Huffman Coding:
Huffman coding is a data compression technique based on greedy choices. Although not directly linked to activity selection, it showcases the power of greedy algorithms in optimization problems.
The essence of the Greedy Algorithm lies in making locally optimal choices to achieve a globally optimal solution. In the Activity Selection Problem, we sort the activities based on their finish times in ascending order. The greedy choice is to select the activity with the earliest finish time, as it leaves room for potential activities later. Let's understand this with an example:
Example:
Consider a set of activities (A, B, C, D) with their respective start and finish times:
A: (1, 3)
B: (2, 5)
C: (4, 7)
D: (6, 9)
The greedy approach would start by selecting activity A, as it has the earliest finish time. Now, A's finish time is 3, so it excludes any activities starting before time 3. Thus, the next selected activity would be C, and the final set of non-conflicting activities is {A, C}.
When the activities are not initially sorted, we can still apply a Greedy Algorithm to solve the problem efficiently. Follow these steps:
1. Sort the activities based on their finish times in ascending order.
2. Initialize an empty set to store the selected activities.
3. Select the first activity with the earliest finish time.
4. Iterate through the sorted activities.
5. For each activity, if its start time is greater than or equal to the finish time of the last selected activity, add it to the set of selected activities.
The Activity Selection Problem comes with specific constraints:
1. Non-negative integers serve as a representation of each activity's start and finish times.
2. The activities are given as a set of pairs, (start_time, finish_time).
3. The start and finish times are inclusive, i.e., the activity can be performed during the entire time interval [start_time, finish_time].
The Greedy Algorithm's primary step is to sort the activities based on their finish times. Then, it iterates through the sorted list, selecting non-conflicting activities as discussed earlier. This approach provides an optimal solution for the Activity Selection Problem, with a time complexity of O(n log n), where n is the number of activities.
Example:
def select_activities_greedy(activities):
activities.sort(key=lambda x: x[1]) # Sort by finish times
selected_activities = [activities[0]]
last_selected = 0
for i in range(1, len(activities)):
if activities[i][0] >= activities[last_selected][1]:
selected_activities.append(activities[i])
last_selected = i
return selected_activities
# Example activities
activities = [(1, 3), (2, 5), (4, 7), (6, 9), (8, 11), (10, 13)]
# Using the Greedy Algorithm
selected_greedy = select_activities_greedy(activities)
# Output
print("Selected Activities using Greedy Algorithm:")
for activity in selected_greedy:
print(activity)
Explanation:
In this code, the select_activities_greedy function uses the Greedy Algorithm to select the maximum number of non-conflicting activities. It sorts the activities by finish times, iterates through them, and selects activities that don't conflict with the previously selected ones. In the example provided, it prints the selected activities:
Output:
Selected Activities using Greedy Algorithm:
(1, 3)
(4, 7)
(8, 11)
Instead of listing selected actions, we compute the maximum number of non-conflicting tasks that can be done at once. The same principle applies: we sort activities by completion date in ascending order and repeat. If an operation's initial time exceeds the last selected operation's completion time, we increase the number of non-contradictory operations. The estimate represents the maximum at the end of the iteration, making it non-contradictory.
Example:
def count_activities(activities):
activities.sort(key=lambda x: x[1]) # Sort by finish times
count = 1
last_selected = 0
for i in range(1, len(activities)):
if activities[i][0] >= activities[last_selected][1]:
count += 1
last_selected = i
return count
# Example activities
activities = [(1, 3), (2, 5), (4, 7), (6, 9), (8, 11), (10, 13)]
# Count the Maximum Number of Non-Conflicting Activities
count = count_activities(activities)
# Output
print(f"Maximum Number of Non-Conflicting Activities: {count}")
Explanation:
In this code, the count_activities function counts the maximum number of non-conflicting activities without storing the activities themselves. It sorts the activities, counts them based on the Greedy Algorithm logic, and returns the count. In the example provided, it prints the count:
Output:
Maximum Number of Non-Conflicting Activities: 3
This approach is similar to Approach 2, but instead of counting the maximum number of non-conflicting activities, we directly print them as they are selected. As we iterate through the sorted activities, we print it out immediately whenever we find a non-conflicting activity.
Example:
def print_max_activities(activities):
activities.sort(key=lambda x: x[1]) # Sort by finish times
last_selected = 0
selected_indices = [last_selected]
for i in range(1, len(activities)):
if activities[i][0] >= activities[last_selected][1]:
selected_indices.append(i)
last_selected = i
print(f"Maximum Number of Non-Conflicting Activities: {len(selected_indices)}")
print("Selected Activities:")
for idx in selected_indices:
print(activities[idx])
# Example activities
activities = [(1, 3), (2, 5), (4, 7), (6, 9), (8, 11), (10, 13)]
# Print the Maximum Number of Non-Conflicting Activities
print_max_activities(activities)
Explanation:
In this code, the print_max_activities function prints both the count and the selected activities without storing them in a separate list. It sorts the activities, keeps track of the selected indices, and prints the count along with the selected activities. In the example provided, it prints both the count and the selected activities:
Output:
Maximum Number of Non-Conflicting Activities: 3
Selected Activities:
(1, 3)
(4, 7)
(8, 11)
In the Dynamic Programming Activity Selection Problem solution, a table (dp array) maintains the maximum number of non-conflicting activities per index. Sorting by finish time and initialising dp[0] to 1 provides the subproblem base case.
Next, we compare all past activities to determine the one with the longest finish time that doesn't clash with the present activity. The dp array stores the maximum number of non-conflicting activities up to the index. The set's maximum non-conflicting activities define dp. To get the best subset, we backtrack from dp[n-1] (where n is the number of activities) using dp values. Dynamic Programming easily integrates Activity Selection Problem subproblems for optimal solutions.
The Activity Selection Problem in C++ includes choosing the most non-conflicting activities from a set. The start_time and finish_time of each activity are integers. The purpose is to discover the best subset of activities that can be done in the timeframe without conflicts.
Greedy Algorithms can efficiently answer the Activity Selection Problem. The Greedy Approach sorts activities by finish time and chooses the most non-conflicting ones.
Example:
```cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Activity {
int start_time;
int finish_time;
};
bool sortByFinishTime(const Activity& a, const Activity& b) {
return a.finish_time < b.finish_time;
}
vector<Activity> selectActivities(vector<Activity>& activities) {
vector<Activity> selected_activities;
sort(activities.begin(), activities.end(), sortByFinishTime);
selected_activities.push_back(activities[0]);
int last_selected = 0;
for (int i = 1; i < activities.size(); i++) {
if (activities[i].start_time >= activities[last_selected].finish_time) {
selected_activities.push_back(activities[i]);
last_selected = i;
}
}
return selected_activities;
}
int main() {
vector<Activity> activities = { {1, 3}, {2, 5}, {4, 7}, {6, 9} };
vector<Activity> selected_activities = selectActivities(activities);
cout << "Selected Activities: ";
for (const Activity& activity : selected_activities) {
cout << "(" << activity.start_time << ", " << activity.finish_time << ") ";
}
cout << endl;
return 0;
}
```
Explanation:
In this C++ implementation, we define a struct `Activity` for each activity with start_time and finish_time characteristics. We then construct a custom comparator function `sortByFinishTime` to ascendingly sort the activities by finish time.
SelectActivities uses the Greedy Approach to select activities from a vector of activities. It arranges the activities by completion time, iterates through them, and selects the non-conflicting ones by comparing their start times to the last picked one.
The `main` function creates a vector of activities and calls the `selectActivities` function to get the best non-conflicting activities. Finally, we publish selected activities to the console.
This C++ code efficiently solves the Activity Selection Problem and shows how the Greedy Algorithm may optimize scheduling and resource allocation.
Time Complexity:
The code arranges activities by completion dates using the std::sort function. Sorting takes O(n log n) time, where 'n' is the number of activities. This is because QuickSort, used by std::sort in C++, requires O(n log n) time to sort a vector of 'n' entries.
The function iterates through sorted activities once after categorising. This loop runs all 'n' activities once, making it O(n).
The most time-consuming part is sorting. Since it sorts, this algorithm takes O(n log n) time.
Space Complexity:
The activities vector contains input activities, while the selected_activities vector contains chosen activities. These vectors have O(n) space complexity, where 'n' is the number of activities.
Other code variables like last_selected and loop indices use a fixed amount of space regardless of input size. Thus, they barely affect space complexity.
This algorithm has O(n) space and O(n log n) time complexity due to the sorting operation and two vectors that grow linearly with the number of activities.
In Java, we can represent each activity using a class or a data structure, such as an Activity class with start_time and finish_time attributes. We can use the Collections.sort method with a custom comparator to sort the activities based on finish times. The Greedy Algorithm can then be applied to select the maximum number of non-conflicting activities.
Example:
```java
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Activity {
int start_time;
int finish_time;
public Activity(int start_time, int finish_time) {
this.start_time = start_time;
this.finish_time = finish_time;
}
}
public class ActivitySelection {
public static List<Activity> selectActivities(List<Activity> activities) {
List<Activity> selectedActivities = new ArrayList<>();
Collections.sort(activities, Comparator.comparingInt(a -> a.finish_time));
selectedActivities.add(activities.get(0));
int lastSelected = 0;
for (int i = 1; i < activities.size(); i++) {
if (activities.get(i).start_time >= activities.get(lastSelected).finish_time) {
selectedActivities.add(activities.get(i));
lastSelected = i;
}
}
return selectedActivities;
}
public static void main(String[] args) {
List<Activity> activities = new ArrayList<>();
activities.add(new Activity(1, 3));
activities.add(new Activity(2, 5));
activities.add(new Activity(4, 7));
activities.add(new Activity(6, 9));
List<Activity> selectedActivities = selectActivities(activities);
System.out.print("Selected Activities: ");
for (Activity activity : selectedActivities) {
System.out.print("(" + activity.start_time + ", " + activity.finish_time + ") ");
}
System.out.println();
}
}
```
Explanation:
In Java, we define a "Activity" class with "start_time" and "finish_time" attributes for each activity. We then sort the activities by finish time using `Comparator.comparingInt`.
SelectActivities uses the Greedy Approach to select activities from a list. It arranges the activities by completion time, iterates through them, and selects the non-conflicting ones by comparing their start times to the last picked one.
The `main` method creates a list of activities and calls the `selectActivities` method to get the best non-conflicting subset. Finally, we publish selected activities to the console.
This Java version efficiently solves the Activity Selection Problem and shows how the Greedy Algorithm may be used in Java to optimize scheduling and resource allocation.
We can represent each activity in Python as a tuple with (start_time, finish_time) elements. We can use the sorted function with a custom key to sort the activities based on finish times. The Greedy Algorithm can then be implemented to select the maximum number of non-conflicting activities.
Example:
def select_activities(activities):
activities.sort(key=lambda x: x[1])
selected_activities = [activities[0]]
last_selected = 0
for i in range(1, len(activities)):
if activities[i][0] >= activities[last_selected][1]:
selected_activities.append(activities[i])
last_selected = i
return selected_activities
activities = [(1, 3), (2, 5), (4, 7), (6, 9), (8, 11), (10, 13)]
selected_activities = select_activities(activities)
print("Selected Activities:", selected_activities)
Explanation:
In Python, select_activities takes a list of activities as input. `(start_time, finish_time)` represents each action. The Greedy Algorithm selects the most non-conflicting tasks.
The `select_activities` function sorts activities by end time using the `sort` method and a custom `key` function that retrieves the finish time from each activity tuple.
The function then initializes an empty list `selected_activities` to store the selected activities. It then adds the first activity with the earliest finish time to the specified activities.
From the second activity onward, the function loops. It compares each activity's start time to the last specified activity's finish time. If this criterion is met, the activity is non-conflicting and can be added to the specified list.
Finally, the function returns the list of selected activities, which is the maximum number of non-conflicting actions that can be done in the timeframe.
In the example provided, the `activities` list contains six activities:
After applying the `select_activities` function, the output will be:
These selected activities represent the maximum number of non-conflicting activities that can be performed within the given time frame, which is the desired solution to the Activity Selection Problem.
In this tutorial, you've navigated the Activity Selection Problem and mastered the use of Greedy Algorithms and Dynamic Programming to solve it efficiently. With this information, you are now prepared to tackle a variety of optimization problems in computer science and beyond. Remember to carefully examine the problem restrictions and select the best technique for each circumstance. Good luck with your problem-solving!
1. Can Greedy Algorithms be applied to solve any optimization problem?
Greedy Algorithms are potent instruments for solving a variety of optimization problems, but they may not always provide the optimal solution for every issue. To assure the algorithm's efficacy, thorough analysis and proof of correctness are required.
2. What is the difference between Greedy Algorithms and Dynamic Programming?
Greedy Algorithms choose the best option at each step, whereas Dynamic Programming divides a problem into overlapping subproblems and caches their answers. Greedy Algorithms do not always produce the best global answer, whereas Dynamic Programming does by examining all options.
3. What is the time complexity of the Dynamic Programming approach for the Activity Selection Problem?
The time complexity of the Dynamic Programming approach is O(n^2), where n is the number of activities. The caching of subproblem solutions eliminates redundant computations and leads to an optimal solution.
Author
Talk to our experts. We are available 7 days a week, 9 AM to 12 AM (midnight)
Indian Nationals
1800 210 2020
Foreign Nationals
+918045604032
1.The above statistics depend on various factors and individual results may vary. Past performance is no guarantee of future results.
2.The student assumes full responsibility for all expenses associated with visas, travel, & related costs. upGrad does not provide any a.