Given a Directed Acyclic Graph (DAG) with V vertices and E edges represented by a 2D array edges[][], where edges[i] = {u, v} represents a directed edge from vertex u to vertex v.
The graph satisfies the following properties:
- If there is a directed edge from u to v, then there is no directed edge from v to u.
- If there is a directed edge from u to v and a directed edge from v to w, then there is also a directed edge from u to w.
Find the minimum number of colors required to color the graph such that no two vertices connected by a directed edge have the same color.
Examples:
Input: V = 5, E = 6, edges[][] = {{1, 3}, {2, 3}, {3, 4}, {1, 4}, {2, 5}, {3, 5}}
Output: 3
Explanation: The graph requires 3 colors as vertices 1, 2 can share one color, vertex 3 needs a different color, and vertices 4, 5 need another color.Input: V = 3, E = 2, edges[][] = {{1, 3}, {2, 3}}
Output: 2
Explanation: Vertices 1 and 2 can share one color, while vertex 3 requires a different color, so 2 colors are needed.
Table of Content
Using DFS + Memoization - O(V + E) Time and O(V + E) Space
Since the graph holds transitivity property, all vertices on a directed path must have different colors. Therefore, the result is equal to the length of the longest directed path in the DAG.
We can find this longest path using DFS. For each vertex u, let dp[u] represent the maximum number of vertices in a path starting from u. For every edge u -> v, we can extend the path through v, so dp[u] = max(dp[u], 1 + dp[v]).
A simple DFS may recompute the longest path for the same vertex multiple times. Since the longest path from a vertex depends only on its outgoing neighbours, we store the computed result in dp[u] and reuse it whenever we encounter u again.
- Build the adjacency list of the DAG.
- Create dp[], where dp[u] stores the longest path starting from vertex u.
- For every unvisited vertex, perform DFS.
- In DFS, if dp[u] is already calculated, return it.
- For every adjacent vertex v, update dp[u] as max(dp[u], 1 + DFS(v)).
- Return the maximum value among all dp[u] as the minimum number of colours.
#include <bits/stdc++.h>
using namespace std;
// Returns the longest path starting from vertex u.
int dfs(int u, vector<vector<int>> &adj, vector<int> &dp)
{
// If the result is already calculated, reuse it.
if (dp[u] != -1)
return dp[u];
// The vertex itself forms a path of length 1.
dp[u] = 1;
// Explore all adjacent vertices.
for (int v : adj[u])
{
// Extend the path through vertex v.
dp[u] = max(dp[u], 1 + dfs(v, adj, dp));
}
return dp[u];
}
// Finds the minimum number of colours required.
int minColour(int V, vector<vector<int>> &edges)
{
// Adjacency list representation of the graph.
vector<vector<int>> adj(V + 1);
// Build the directed graph.
for (auto &edge : edges)
{
int u = edge[0];
int v = edge[1];
// Add directed edge u -> v.
adj[u].push_back(v);
}
// dp[u] stores the longest path starting from vertex u.
// -1 means the value has not been calculated yet.
vector<int> dp(V + 1, -1);
int ans = 1;
// Find the longest path starting from every vertex.
for (int u = 1; u <= V; u++)
{
ans = max(ans, dfs(u, adj, dp));
}
// The longest path determines the minimum number of colours.
return ans;
}
int main()
{
int V = 5;
vector<vector<int>> edges = {{1, 3}, {2, 3}};
cout << minColour(V, edges) << endl;
return 0;
}
import java.util.*;
class GFG {
static int dfs(int u, ArrayList<ArrayList<Integer> > adj, int[] dp)
{
// If the result is already calculated, reuse it.
if (dp[u] != -1)
return dp[u];
// The vertex itself forms a path of length 1.
dp[u] = 1;
// Explore all adjacent vertices.
for (int v : adj.get(u)) {
// Extend the path through vertex v.
dp[u] = Math.max(dp[u], 1 + dfs(v, adj, dp));
}
return dp[u];
}
// Finds the minimum number of colours required.
static int minColour(int V, int[][] edges)
{
// Adjacency list representation of the graph.
ArrayList<ArrayList<Integer> > adj
= new ArrayList<>();
for (int i = 0; i <= V; i++)
adj.add(new ArrayList<>());
// Build the directed graph.
for (int i = 0; i < edges.length; i++) {
int u = edges[i][0];
int v = edges[i][1];
// Add directed edge u -> v.
adj.get(u).add(v);
}
// dp[u] stores the longest path starting from
// vertex u. -1 means the value has not been
// calculated yet.
int[] dp = new int[V + 1];
Arrays.fill(dp, -1);
int ans = 1;
// Find the longest path starting from every vertex.
for (int u = 1; u <= V; u++) {
ans = Math.max(ans, dfs(u, adj, dp));
}
// The longest path determines the minimum number of
// colours.
return ans;
}
public static void main(String[] args)
{
int V = 5;
int[][] edges = { { 1, 3 }, { 2, 3 } };
System.out.println(minColour(V, edges));
}
}
# Returns the longest path starting from vertex u.
def dfs(u, adj, dp):
# If the result is already calculated, reuse it.
if dp[u] != -1:
return dp[u]
# The vertex itself forms a path of length 1.
dp[u] = 1
# Explore all adjacent vertices.
for v in adj[u]:
# Extend the path through vertex v.
dp[u] = max(dp[u], 1 + dfs(v, adj, dp))
return dp[u]
# Finds the minimum number of colours required.
def minColour(V, edges):
# Adjacency list representation of the graph.
adj = [[] for _ in range(V + 1)]
# Build the directed graph.
for edge in edges:
u = edge[0]
v = edge[1]
# Add directed edge u -> v.
adj[u].append(v)
# dp[u] stores the longest path starting from vertex u.
# -1 means the value has not been calculated yet.
dp = [-1] * (V + 1)
ans = 1
# Find the longest path starting from every vertex.
for u in range(1, V + 1):
ans = max(ans, dfs(u, adj, dp))
# The longest path determines the minimum number of colours.
return ans
# Driver Code
if __name__ == "__main__":
V = 5
edges = [
[1, 3],
[2, 3]
]
print(minColour(V, edges))
using System;
using System.Collections.Generic;
class GFG {
static int DFS(int u, List<int>[] adj, int[] dp)
{
// If the result is already calculated, reuse it.
if (dp[u] != -1)
return dp[u];
// The vertex itself forms a path of length 1.
dp[u] = 1;
// Explore all adjacent vertices.
foreach(int v in adj[u])
{
// Extend the path through vertex v.
dp[u] = Math.Max(dp[u], 1 + DFS(v, adj, dp));
}
return dp[u];
}
// Finds the minimum number of colours required.
static int minColour(int V, int[, ] edges)
{
// Adjacency list representation of the graph.
List<int>[] adj = new List<int>[ V + 1 ];
for (int i = 0; i <= V; i++)
adj[i] = new List<int>();
// Build the directed graph.
int E = edges.GetLength(0);
for (int i = 0; i < E; i++) {
int u = edges[i, 0];
int v = edges[i, 1];
// Add directed edge u -> v.
adj[u].Add(v);
}
// dp[u] stores the longest path starting from
// vertex u. -1 means the value has not been
// calculated yet.
int[] dp = new int[V + 1];
Array.Fill(dp, -1);
int ans = 1;
// Find the longest path starting from every vertex.
for (int u = 1; u <= V; u++) {
ans = Math.Max(ans, DFS(u, adj, dp));
}
// The longest path determines the minimum number of
// colours.
return ans;
}
static void Main()
{
int V = 5;
int[, ] edges = { { 1, 3 }, { 2, 3 } };
Console.WriteLine(minColour(V, edges));
}
}
// Returns the longest path starting from vertex u.
function dfs(u, adj, dp)
{
// If the result is already calculated, reuse it.
if (dp[u] !== -1)
return dp[u];
// The vertex itself forms a path of length 1.
dp[u] = 1;
// Explore all adjacent vertices.
for (const v of adj[u]) {
// Extend the path through vertex v.
dp[u] = Math.max(dp[u], 1 + dfs(v, adj, dp));
}
return dp[u];
}
// Finds the minimum number of colours required.
function minColour(V, edges)
{
// Adjacency list representation of the graph.
const adj = Array.from({length : V + 1}, () => []);
// Build the directed graph.
for (const edge of edges) {
const u = edge[0];
const v = edge[1];
// Add directed edge u -> v.
adj[u].push(v);
}
// dp[u] stores the longest path starting from vertex u.
// -1 means the value has not been calculated yet.
const dp = new Array(V + 1).fill(-1);
let ans = 1;
// Find the longest path starting from every vertex.
for (let u = 1; u <= V; u++) {
ans = Math.max(ans, dfs(u, adj, dp));
}
// The longest path determines the minimum number of
// colours.
return ans;
}
// Driver Code
const V = 5;
const edges = [ [ 1, 3 ], [ 2, 3 ] ];
console.log(minColour(V, edges));
Output
2
Using Topological Sort - O(V + E) Time and O(V + E) Space
The idea is to avoid recursive DFS and directly computes the longest path while performing Kahn's Topological Sort.
For each vertex u, let dist[u] represent the maximum number of vertices in a path ending at u. Initially, every vertex requires one color. For every edge u -> v, we extend the longest path ending at u by v, so we update dist[v] = max(dist[v], dist[u] + 1).
- Build the adjacency list and calculate the indegree of every vertex.
- Initialize dist[u] = 1 for every vertex and add all vertices with indegree 0 to the queue.
- Process vertices using Kahn's Topological Sort.
- For every edge u -> v, update dist[v] = max(dist[v], dist[u] + 1).
- Decrease the indegree of v; if it becomes 0, add v to the queue.
- The maximum value in dist[] is the minimum number of colours required.
#include <bits/stdc++.h>
using namespace std;
// Finds the minimum number of colours required.
int minColour(int V, vector<vector<int>> &edges)
{
// Adjacency list representation of the graph.
vector<vector<int>> adj(V + 1);
// Stores indegree of each vertex.
vector<int> indeg(V + 1, 0);
// Build the graph and calculate indegrees.
for (auto &edge : edges)
{
int u = edge[0];
int v = edge[1];
// Add directed edge u -> v.
adj[u].push_back(v);
// Increase indegree of v.
indeg[v]++;
}
// Queue for Kahn's Topological Sort.
queue<int> q;
// dist[u] stores the longest path ending at vertex u.
vector<int> dist(V + 1, 1);
// Push all vertices having indegree 0.
for (int u = 1; u <= V; u++)
{
if (indeg[u] == 0)
q.push(u);
}
// Perform Topological Sort.
while (!q.empty())
{
int u = q.front();
q.pop();
// Explore all adjacent vertices.
for (int v : adj[u])
{
// Extend the longest path through vertex v.
dist[v] = max(dist[v], dist[u] + 1);
// Reduce indegree of v.
indeg[v]--;
// Add v when all its predecessors are processed.
if (indeg[v] == 0)
q.push(v);
}
}
// Find the maximum colour required.
int ans = 1;
for (int u = 1; u <= V; u++)
{
ans = max(ans, dist[u]);
}
return ans;
}
int main()
{
int V = 5;
vector<vector<int>> edges = {{1, 3}, {2, 3}};
cout << minColour(V, edges) << endl;
return 0;
}
import java.util.*;
class GFG {
static int minColour(int V, int[][] edges)
{
// Adjacency list representation of the graph.
ArrayList<ArrayList<Integer> > adj
= new ArrayList<>();
for (int i = 0; i <= V; i++) {
adj.add(new ArrayList<>());
}
// Stores indegree of each vertex.
int[] indeg = new int[V + 1];
// Build the graph and calculate indegrees.
for (int[] edge : edges) {
int u = edge[0];
int v = edge[1];
// Add directed edge u -> v.
adj.get(u).add(v);
// Increase indegree of v.
indeg[v]++;
}
// Queue for Kahn's Topological Sort.
Queue<Integer> q = new LinkedList<>();
// dist[u] stores the longest path ending at vertex
// u.
int[] dist = new int[V + 1];
Arrays.fill(dist, 1);
// Push all vertices having indegree 0.
for (int u = 1; u <= V; u++) {
if (indeg[u] == 0)
q.add(u);
}
// Perform Topological Sort.
while (!q.isEmpty()) {
int u = q.poll();
// Explore all adjacent vertices.
for (int v : adj.get(u)) {
// Extend the longest path through vertex v.
dist[v] = Math.max(dist[v], dist[u] + 1);
// Reduce indegree of v.
indeg[v]--;
// Add v when all its predecessors are
// processed.
if (indeg[v] == 0)
q.add(v);
}
}
// Find the maximum colour required.
int ans = 1;
for (int u = 1; u <= V; u++) {
ans = Math.max(ans, dist[u]);
}
return ans;
}
public static void main(String[] args)
{
int V = 5;
int[][] edges = { { 1, 3 }, { 2, 3 } };
System.out.println(minColour(V, edges));
}
}
from collections import deque
# Finds the minimum number of colours required.
def minColour(V, edges):
# Adjacency list representation of the graph.
adj = [[] for _ in range(V + 1)]
# Stores indegree of each vertex.
indeg = [0] * (V + 1)
# Build the graph and calculate indegrees.
for edge in edges:
u = edge[0]
v = edge[1]
# Add directed edge u -> v.
adj[u].append(v)
# Increase indegree of v.
indeg[v] += 1
# Queue for Kahn's Topological Sort.
q = deque()
# dist[u] stores the longest path ending at vertex u.
dist = [1] * (V + 1)
# Push all vertices having indegree 0.
for u in range(1, V + 1):
if indeg[u] == 0:
q.append(u)
# Perform Topological Sort.
while q:
u = q.popleft()
# Explore all adjacent vertices.
for v in adj[u]:
# Extend the longest path through vertex v.
dist[v] = max(dist[v], dist[u] + 1)
# Reduce indegree of v.
indeg[v] -= 1
# Add v when all its predecessors are processed.
if indeg[v] == 0:
q.append(v)
# Find the maximum colour required.
ans = 1
for u in range(1, V + 1):
ans = max(ans, dist[u])
return ans
# Driver Code
if __name__ == "__main__":
V = 5
edges = [
[1, 3],
[2, 3]
]
print(minColour(V, edges))
using System;
using System.Collections.Generic;
class GFG {
static int minColour(int V, int[, ] edges)
{
// Adjacency list representation of the graph.
List<int>[] adj = new List<int>[ V + 1 ];
for (int i = 0; i <= V; i++) {
adj[i] = new List<int>();
}
// Stores indegree of each vertex.
int[] indeg = new int[V + 1];
// Build the graph and calculate indegrees.
int E = edges.GetLength(0);
for (int i = 0; i < E; i++) {
int u = edges[i, 0];
int v = edges[i, 1];
// Add directed edge u -> v.
adj[u].Add(v);
// Increase indegree of v.
indeg[v]++;
}
// Queue for Kahn's Topological Sort.
Queue<int> q = new Queue<int>();
// dist[u] stores the longest path ending at vertex
// u.
int[] dist = new int[V + 1];
for (int i = 0; i <= V; i++) {
dist[i] = 1;
}
// Push all vertices having indegree 0.
for (int u = 1; u <= V; u++) {
if (indeg[u] == 0)
q.Enqueue(u);
}
// Perform Topological Sort.
while (q.Count > 0) {
int u = q.Dequeue();
// Explore all adjacent vertices.
foreach(int v in adj[u])
{
// Extend the longest path through vertex v.
dist[v] = Math.Max(dist[v], dist[u] + 1);
// Reduce indegree of v.
indeg[v]--;
// Add v when all its predecessors are
// processed.
if (indeg[v] == 0)
q.Enqueue(v);
}
}
// Find the maximum colour required.
int ans = 1;
for (int u = 1; u <= V; u++) {
ans = Math.Max(ans, dist[u]);
}
return ans;
}
static void Main()
{
int V = 5;
int[, ] edges = { { 1, 3 }, { 2, 3 } };
Console.WriteLine(minColour(V, edges));
}
}
// Finds the minimum number of colours required.
function minColour(V, edges)
{
// Adjacency list representation of the graph.
const adj = Array.from({length : V + 1}, () => []);
// Stores indegree of each vertex.
const indeg = new Array(V + 1).fill(0);
// Build the graph and calculate indegrees.
for (const edge of edges) {
const u = edge[0];
const v = edge[1];
// Add directed edge u -> v.
adj[u].push(v);
// Increase indegree of v.
indeg[v]++;
}
// Queue for Kahn's Topological Sort.
const q = [];
// dist[u] stores the longest path ending at vertex u.
const dist = new Array(V + 1).fill(1);
// Push all vertices having indegree 0.
for (let u = 1; u <= V; u++) {
if (indeg[u] === 0)
q.push(u);
}
let front = 0;
// Perform Topological Sort.
while (front < q.length) {
const u = q[front++];
// Explore all adjacent vertices.
for (const v of adj[u]) {
// Extend the longest path through vertex v.
dist[v] = Math.max(dist[v], dist[u] + 1);
// Reduce indegree of v.
indeg[v]--;
// Add v when all its predecessors are
// processed.
if (indeg[v] === 0)
q.push(v);
}
}
// Find the maximum colour required.
let ans = 1;
for (let u = 1; u <= V; u++) {
ans = Math.max(ans, dist[u]);
}
return ans;
}
// Driver Code
const V = 5;
const edges = [ [ 1, 3 ], [ 2, 3 ] ];
console.log(minColour(V, edges));
Output
2