Minimize number of notes required to be distributed among students
Given an array arr[] consisting of N strings representing the name of the students in the class and another array of pairs P[][2] such that P[i][0] likes P[i][1], the task is to find the minimum number of notes to be distributed in the class such that sharing of notes can take place only if a student likes another student either directly or indirectly.
Examples:
Input: arr[] = {geeks, for, code, run, compile}, P[][] = {{geeks, for}, {for, code}, {code, run}, {run, compile}, {run, for}}
Output: 3
Explanation:
Below is the image to represent the relationship among the students:
From the above image:
- Students named {“for”, “code”, “run”} require a single copy of notes, since there exists a mutual relationship between them.
- Student named {“geeks”} requires a single copy of notes.
- Student named {“compile”} also require a single copy of notes.
So, the minimum number of notes required is 3.
Input: arr[] = {geeks, for, all, run, debug, compile}, P[][] = {{geeks, for}, {for, all}, {all, geeks}, {for, run}, {run, compile}, {compile, debug}, {debug, run}}
Output: 2
Approach: The given problem can be solved by finding the number of strongly connected components in a directed graph after generating the relationship graph with the given conditions. Follow the steps below to solve the problem:
- Create a hashmap, say M to map the names of the students to their respective index values.
- Traverse the array A using the variable i and map each string A[i] in the map M to value i.
- Iterate all the pairs in the array P and for each pair get the corresponding values from the HashMap, M and create a directed edge between them.
- After traversing all the pairs, a directed graph is formed having N vertices and M number of edges.
- Create an empty stack S, and perform the DFS traversal of the graph:
- Create a recursive function that takes the index of a node and a visited array.
- Mark the current node as visited and traverse all the adjacent and unmarked nodes and call the recursive function with the index of the adjacent node.
- After traversing all the neighbors of the current node, push the current node in the stack S.
- Reverse directions of all edges to obtain the transpose of the constructed graph.
- Iterate until the stack S is not empty and perform the following steps:
- Store the top element of the stack S in a variable V and pop it from the stack S.
- Perform the DFS traversal from node V as the source.
- Update the number of connected components and store the count in a variable, sat cnt.
- After completing the above steps, print the value of cnt as the result.
Below is the implementation of the above approach:
C++
// C++ program for the above approach #include <bits/stdc++.h> using namespace std; // Structure of class Graph class Graph { // No. of vertices int V; // An array of adjacency lists list< int >* adj; // Function that fills the stack // with the vertices v void fillOrder( int v, bool visited[], stack< int >& Stack); // Recursive function to perform // the DFS starting from v void DFSUtil( int v, bool visited[]); public : Graph( int V); void addEdge( int v, int w); // Function to count the number of // strongly connected components void countSCCs(); // Function that returns reverse // (or transpose) of the graph Graph getTranspose(); }; // Constructor of the Graph Graph::Graph( int V) { this ->V = V; adj = new list< int >[V]; } // Recursive function to perform the // DFS starting from v void Graph::DFSUtil( int v, bool visited[]) { // Mark the current node as visited visited[v] = true ; // Recurr for all the vertices // adjacent to this vertex list< int >::iterator i; for (i = adj[v].begin(); i != adj[v].end(); ++i) { if (!visited[*i]) DFSUtil(*i, visited); } } // Function to return the reverse // (or transpose) of the graph Graph Graph::getTranspose() { Graph g(V); for ( int v = 0; v < V; v++) { // Recurr for all the vertices // adjacent to this vertex list< int >::iterator i; for (i = adj[v].begin(); i != adj[v].end(); ++i) { g.adj[*i].push_back(v); } } return g; } // Function to add an edge void Graph::addEdge( int v, int w) { // Add w to v’s list adj[v].push_back(w); } // Function to fill the stack with // the vertices during DFS traversal void Graph::fillOrder( int v, bool visited[], stack< int >& Stack) { // Mark the current node as visited visited[v] = true ; // Recurr for all the vertices // adjacent to this vertex list< int >::iterator i; for (i = adj[v].begin(); i != adj[v].end(); ++i) { if (!visited[*i]) fillOrder(*i, visited, Stack); } // All vertices reachable from // the node v are processed // Update the stack Stack.push(v); } // Function that counts the strongly // connected components in the graph void Graph::countSCCs() { stack< int > Stack; // Mark all the vertices as not // visited (For first DFS) bool * visited = new bool [V]; for ( int i = 0; i < V; i++) visited[i] = false ; // Fill vertices in the stack // according to their finishing // time for ( int i = 0; i < V; i++) { // Vertex i is not visited if (visited[i] == false ) fillOrder(i, visited, Stack); } // Create a reversed graph Graph gr = getTranspose(); // Mark all the vertices as // not visited (For second DFS) for ( int i = 0; i < V; i++) visited[i] = false ; int cnt = 0; // Now process all vertices in // order defined by Stack while (Stack.empty() == false ) { // Pop a vertex from stack int v = Stack.top(); Stack.pop(); // Get the strongly connected // component of the popped // vertex if (visited[v] == false ) { gr.DFSUtil(v, visited); cnt++; } } // Print the result cout << cnt; } // Function that counts the minimum // number of notes required with the // given criteria void solve(vector<string>& A, vector<vector<string> >& P) { Graph g(A.size()); // Used to map the strings to // their respective indices unordered_map<string, int > um; for ( int i = 0; i < A.size(); i++) { um[A[i]] = i; } // Iterate through all the edges // and add them to the graph for ( int i = 0; i < P.size(); i++) { int x = um[P[i][0]]; int y = um[P[i][1]]; g.addEdge(x, y); } // Function Call g.countSCCs(); } // Driver Code int main() { vector<string> arr = { "geeks" , "for" , "code" , "run" , "compile" }; vector<vector<string> > P = { { "geeks" , "for" }, { "for" , "code" }, { "code" , "run" }, { "run" , "compile" }, { "run" , "for" } }; solve(arr, P); return 0; } |
Java
// Java program for above approach import java.util.ArrayList; import java.util.*; // Structure of class Graph public class Graph{ // No. of vertices int V; // An array of adjacency lists ArrayList<ArrayList<Integer>> adj; // Constructor of the Graph Graph( int V) { this .V = V; adj = new ArrayList<>(); for ( int i = 0 ; i < V; i++) { adj.add( new ArrayList<>()); } } // Recursive function to perform the // DFS starting from v void DFSUtil( int v, boolean visited[]) { // Mark the current node as visited visited[v] = true ; // Recurr for all the vertices // adjacent to this vertex for ( int i : adj.get(v)) { if (!visited[i]) DFSUtil(i, visited); } } // Function to return the reverse // (or transpose) of the graph Graph getTranspose() { Graph g = new Graph(V); for ( int v = 0 ; v < V; v++) { // Recurr for all the vertices // adjacent to this vertex for ( int i : adj.get(v)) { g.adj.get(i).add(v); } } return g; } // Function to add an edge void addEdge( int v, int w) { // Add w to v’s list adj.get(v).add(w); } // Function to fill the stack with // the vertices during DFS traversal void fillOrder( int v, boolean [] visited, Stack<Integer> stack) { // Mark the current node as visited visited[v] = true ; // Recurr for all the vertices // adjacent to this vertex for ( int i : adj.get(v)) { if (!visited[i]) fillOrder(i, visited, stack); } // All vertices reachable from // the node v are processed // Update the stack stack.push(v); } // Function that counts the strongly // connected components in the graph void countSCCs() { Stack<Integer> stack = new Stack<>(); // Mark all the vertices as not // visited (For first DFS) boolean [] visited = new boolean [V]; for ( int i = 0 ; i < V; i++) visited[i] = false ; // Fill vertices in the stack // according to their finishing // time for ( int i = 0 ; i < V; i++) { // Vertex i is not visited if (visited[i] == false ) fillOrder(i, visited, stack); } // Create a reversed graph Graph gr = getTranspose(); // Mark all the vertices as // not visited (For second DFS) for ( int i = 0 ; i < V; i++) visited[i] = false ; int cnt = 0 ; // Now process all vertices in // order defined by Stack while (stack.empty() == false ) { // Pop a vertex from stack int v = stack.peek(); stack.pop(); // Get the strongly connected // component of the popped // vertex if (visited[v] == false ) { gr.DFSUtil(v, visited); cnt++; } } // Print the result System.out.print(cnt); } // Function that counts the minimum // number of notes required with the // given criteria static void solve(ArrayList<String> A, ArrayList<ArrayList<String>> P) { Graph g = new Graph(A.size()); // Used to map the strings to // their respective indices HashMap<String, Integer> um = new HashMap<>(); for ( int i = 0 ; i < A.size(); i++) { um.put(A.get(i), i); } // Iterate through all the edges // and add them to the graph for ( int i = 0 ; i < P.size(); i++) { int x = um.get(P.get(i).get( 0 )); int y = um.get(P.get(i).get( 1 )); g.addEdge(x, y); } // Function Call g.countSCCs(); } // Driver code public static void main(String[] args) { ArrayList<String> arr = new ArrayList<>(); arr.add( "geeks" ); arr.add( "for" ); arr.add( "code" ); arr.add( "run" ); arr.add( "compile" ); ArrayList<ArrayList<String> > P = new ArrayList<>(); for ( int i = 0 ; i < 5 ; i++) P.add( new ArrayList<>()); P.get( 0 ).add( "geeks" ); P.get( 0 ).add( "for" ); P.get( 1 ).add( "for" ); P.get( 1 ).add( "code" ); P.get( 2 ).add( "code" ); P.get( 2 ).add( "run" ); P.get( 3 ).add( "run" ); P.get( 3 ).add( "compile" ); P.get( 4 ).add( "run" ); P.get( 4 ).add( "for" ); solve(arr, P); } } // This code is contributed by hritikrommie |
3
Time Complexity: O(N + M)
Auxiliary Space: O(N)