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++ 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;
// Recur 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++) {
// Recur 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;
// Recur 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 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;
// Recur 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++)
{
// Recur 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;
// Recur 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
# Python 3 program to implement the approach
# Class structure for Graph
class Graph:
# Constructor of the Graph
def __init__(self, V):
# No. of vertices
self.V = V
# An array of adjacency lists
self.adj = [[] for i in range(V)]
# Recursive function to perform the DFS starting from v
def DFSUtil(self, v, visited):
visited[v] = True
for i in self.adj[v]:
if not visited[i]:
self.DFSUtil(i, visited)
# Function that returns reverse (or transpose) of the graph
def getTranspose(self):
g = Graph(self.V)
for v in range(self.V):
for i in self.adj[v]:
g.adj[i].append(v)
return g
# Function to add an edge to the graph
def addEdge(self, v, w):
self.adj[v].append(w)
# Function that fills the stack with the vertices v
def fillOrder(self, v, visited, stack):
visited[v] = True
for i in self.adj[v]:
if not visited[i]:
self.fillOrder(i, visited, stack)
stack.append(v)
# Function to count the number of strongly connected components
def countSCCs(self):
stack = []
# Mark all the vertices as not visited (For first DFS)
visited = [False for i in range(self.V)]
# Fill vertices in the stack according to their finishing time
for i in range(self.V):
if not visited[i]:
self.fillOrder(i, visited, stack)
# Create a reversed graph
gr = self.getTranspose()
# Mark all the vertices as not visited (For second DFS)
visited = [False for i in range(self.V)]
# Initialize the count of strongly connected components
cnt = 0
# Now process all vertices in order defined by Stack
while stack:
# Pop a vertex from stack
v = stack.pop()
# Get the strongly connected component of the popped vertex
if not visited[v]:
gr.DFSUtil(v, visited)
cnt += 1
# Return the result
return cnt
# Function that counts the minimum number of notes required with the given criteria
def solve(A, P):
# Used to map the strings to their respective indices
um = {}
for i, a in enumerate(A):
um[a] = i
g = Graph(len(A))
# Iterate through all the edges and add them to the graph
for p in P:
x = um[p[0]]
y = um[p[1]]
g.addEdge(x, y)
print(g.countSCCs())
# Driver Code
arr = ["geeks", "for", "code", "run", "compile"]
P = [
["geeks", "for"], ["for", "code"],
["code", "run"], ["run", "compile"],
["run", "for"]
]
# Function call
solve(arr, P)
# This code is contributed by phasing17
// C# code to implement the approach
using System;
using System.Collections.Generic;
// Structure of class Graph
public class Graph {
// No. of vertices
int V;
// An array of adjacency lists
List<List<int> > adj;
public Graph(int V)
{
this.V = V;
adj = new List<List<int> >();
for (int i = 0; i < V; i++) {
adj.Add(new List<int>());
}
}
// Recursive function to perform
// the DFS starting from v
void DFSUtil(int v, bool[] visited)
{
visited[v] = true;
foreach(int i in adj[v])
{
if (!visited[i]) {
DFSUtil(i, visited);
}
}
}
// Function that returns reverse
// (or transpose) of the graph
Graph GetTranspose()
{
Graph g = new Graph(V);
for (int v = 0; v < V; v++) {
foreach(int i in adj[v]) { g.adj[i].Add(v); }
}
return g;
}
void AddEdge(int v, int w) { adj[v].Add(w); }
// Function that fills the stack
// with the vertices v
void FillOrder(int v, bool[] visited, Stack<int> stack)
{
visited[v] = true;
foreach(int i in adj[v])
{
if (!visited[i]) {
FillOrder(i, visited, stack);
}
}
stack.Push(v);
}
// Function to count the number of
// strongly connected components
void CountSCCs()
{
Stack<int> stack = new Stack<int>();
bool[] visited = new bool[V];
for (int i = 0; i < V; i++) {
visited[i] = false;
}
for (int i = 0; i < V; i++) {
if (!visited[i]) {
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.Count > 0) {
// Pop a vertex from stack
int v = stack.Peek();
stack.Pop();
// Get the strongly connected
// component of the popped
// vertex
if (!visited[v]) {
gr.DFSUtil(v, visited);
cnt++;
}
}
// Print the result
Console.WriteLine(cnt);
}
// Function that counts the minimum
// number of notes required with the
// given criteria
public static void Solve(List<string> A,
List<List<string> > P)
{
Graph g = new Graph(A.Count);
// Used to map the strings to
// their respective indices
Dictionary<string, int> um
= new Dictionary<string, int>();
for (int i = 0; i < A.Count; i++) {
um[A[i]] = i;
}
// Iterate through all the edges
// and add them to the graph
for (int i = 0; i < P.Count; i++) {
int x = um[P[i][0]];
int y = um[P[i][1]];
g.AddEdge(x, y);
}
g.CountSCCs();
}
// Driver code
static void Main(string[] args)
{
List<string> A
= new List<string>{ "geeks", "for", "code",
"run", "compile" };
List<List<string> > P = new List<List<string> >{
new List<string>{ "geeks", "for" },
new List<string>{ "for", "code" },
new List<string>{ "code", "run" },
new List<string>{ "run", "compile" },
new List<string>{ "run", "for" },
};
// Function call
Solve(A, P);
}
}
// This code is contributed by phasing17
// JS program to implement the approach
// Structure of class Graph
const Graph =
// Constructor of the Graph
function(V)
{
// No. of vertices
this.V = V;
// An array of adjacency lists
this.adj = [];
for (let i = 0; i < V; i++) {
this.adj.push([]);
}
};
// Recursive function to perform
// the DFS starting from v
Graph.prototype.DFSUtil = function(v, visited)
{
visited[v] = true;
for (let i of this.adj[v]) {
if (!visited[i]) {
this.DFSUtil(i, visited);
}
}
};
// Function that returns reverse
// (or transpose) of the graph
Graph.prototype.getTranspose = function()
{
const g = new Graph(this.V);
for (let v = 0; v < this.V; v++) {
for (let i of this.adj[v]) {
g.adj[i].push(v);
}
}
return g;
};
Graph.prototype.addEdge = function(v, w)
{
this.adj[v].push(w);
};
// Function that fills the stack
// with the vertices v
Graph.prototype.fillOrder = function(v, visited, stack)
{
visited[v] = true;
for (let i of this.adj[v]) {
if (!visited[i]) {
this.fillOrder(i, visited, stack);
}
}
stack.push(v);
};
// Function to count the number of
// strongly connected components
Graph.prototype.countSCCs = function()
{
const stack = [];
// Mark all the vertices as not
// visited (For first DFS)
const visited = Array(this.V).fill(false);
// Fill vertices in the stack
// according to their finishing
// time
for (let i = 0; i < this.V; i++) {
if (!visited[i]) {
this.fillOrder(i, visited, stack);
}
}
// Create a reversed graph
const gr = this.getTranspose();
// Mark all the vertices as
// not visited (For second DFS)
visited.fill(false);
let cnt = 0;
// Now process all vertices in
// order defined by Stack
while (stack.length > 0) {
// Pop a vertex from stack
const v = stack.pop();
// Get the strongly connected
// component of the popped
// vertex
if (!visited[v]) {
gr.DFSUtil(v, visited);
cnt++;
}
}
// Print the result
console.log(cnt);
};
// Function that counts the minimum
// number of notes required with the
// given criteria
const solve = function(A, P)
{
// Used to map the strings to
// their respective indices
const um = new Map();
for (let i = 0; i < A.length; i++) {
um.set(A[i], i);
}
const g = new Graph(A.length);
// Iterate through all the edges
// and add them to the graph
for (let i = 0; i < P.length; i++) {
const x = um.get(P[i][0]);
const y = um.get(P[i][1]);
g.addEdge(x, y);
}
g.countSCCs();
};
// Driver Code
let arr = [ "geeks", "for", "code", "run", "compile" ];
let P = [
[ "geeks", "for" ], [ "for", "code" ],
[ "code", "run" ], [ "run", "compile" ],
[ "run", "for" ]
];
// Function call
solve(arr, P);
// This code is contributed by phasing17
Output:
3
Time Complexity: O(N + M)
Auxiliary Space: O(N)
