Given a graph consisting of N nodes, where each node represents an exam and a 2D array Edges[][2] such that each pair of the exam (Edges[i][0], Edges[i][1]) denotes the edge between them, the task is to find the minimum number of days required to schedule all the exams such that no two exams connected via an edge are scheduled on the same day.
Examples:
Input: N = 5, E = 10, Edges[][] = {{0, 1}, {0, 2}, {0, 3}, {0, 4}, {1, 2}, {1, 3}, {1, 4}, {2, 3}, {2, 4}, {3, 4}}
Output: 5
Explanation:
In the above graph, all the nodes (representing exams) are connected to each other via a directed path. Therefore, the minimum number of days required to complete the exam is 5.
Input: N = 7, E = 12, Edges[][] = [{0, 1}, {0, 3}, {0, 4}, {0, 6}, {1, 2}, {1, 4}, {1, 6}, {2, 5}, {2, 6}, {3, 4}, {3, 5}, {4, 5}]
Output: 4
[Approach] Inclusion-Exclusion algorithm
To find the absolute minimum number of days, we use the Inclusion-Exclusion Principle to calculate the Chromatic Polynomial. We first use Sum Over Subsets (SOS) DP in O(V x 2^V) to count how many independent sets exist within every possible vertex subset. Finally, we check increasing values of k until the formula yields a non-zero number of valid colorings, guaranteeing the Chromatic Number is found.
Below is the implementation of the above approach:
#include <iostream>
#include <vector>
using namespace std;
int solveChromaticNumber(int V, const vector<int> &adj_mask)
{
if (V == 0)
return 0;
// Use a large prime modulus to handle astronomical counts and prevent overflow
const long long MOD = 1e9 + 7;
int num_subsets = 1 << V;
// 1. Identify all independent sets (subsets where no two nodes are connected)
vector<int> I(num_subsets, 0);
I[0] = 1;
for (int mask = 1; mask < num_subsets; ++mask)
{
int v = __builtin_ctz(mask); // Find an arbitrary vertex in the mask
int prev_mask = mask ^ (1 << v);
I[mask] = I[prev_mask] && !(adj_mask[v] & prev_mask);
}
// 2. SOS DP (Sum Over Subsets) to count independent sets within each subset mask
vector<long long> f(num_subsets, 0);
for (int mask = 0; mask < num_subsets; ++mask)
f[mask] = I[mask];
for (int i = 0; i < V; ++i)
{
for (int mask = 0; mask < num_subsets; ++mask)
{
if (mask & (1 << i))
{
f[mask] = (f[mask] + f[mask ^ (1 << i)]) % MOD;
}
}
}
// 3. Apply Inclusion-Exclusion Principle
// We check if it is possible to color the graph using k colors
for (int k = 1; k <= V; ++k)
{
long long total_ways = 0;
for (int mask = 0; mask < num_subsets; ++mask)
{
int set_bits = __builtin_popcount(mask);
// Calculate f[mask]^k % MOD
long long pow_f = 1;
for (int p = 0; p < k; ++p)
{
pow_f = (pow_f * f[mask]) % MOD;
}
// Inclusion-Exclusion logic: sum (-1)^(|V|-|mask|) * f(mask)^k
if ((V - set_bits) % 2 == 1)
{
total_ways = (total_ways - pow_f + MOD) % MOD;
}
else
{
total_ways = (total_ways + pow_f) % MOD;
}
}
// If total_ways != 0, a valid k-coloring exists, and k is the minimum
if (total_ways != 0)
return k;
}
return V;
}
int main()
{
int V = 7;
vector<pair<int, int>> edges = {{0, 1}, {0, 3}, {0, 4}, {0, 6}, {1, 2}, {1, 4},
{1, 6}, {2, 5}, {2, 6}, {3, 4}, {3, 5}, {4, 5}};
// Construct Adjacency Bitmask
vector<int> adj_mask(V, 0);
for (auto &edge : edges)
{
int u = edge.first;
int v = edge.second;
adj_mask[u] |= (1 << v);
adj_mask[v] |= (1 << u);
}
// Calculate and Output Result
cout << solveChromaticNumber(V, adj_mask) << endl;
return 0;
}
import java.util.*;
public class GFG {
public static int solveChromaticNumber(int V,
int[] adjMask)
{
if (V == 0)
return 0;
long MOD = 1000000007L;
int numSubsets = 1 << V;
// 1. Precompute independent sets
int[] I = new int[numSubsets];
I[0] = 1;
for (int mask = 1; mask < numSubsets; mask++) {
int v = Integer.numberOfTrailingZeros(mask);
int prevMask = mask ^ (1 << v);
I[mask] = (I[prevMask] == 1
&& (adjMask[v] & prevMask) == 0)
? 1
: 0;
}
// 2. SOS DP (Sum Over Subsets)
long[] f = new long[numSubsets];
for (int mask = 0; mask < numSubsets; mask++)
f[mask] = I[mask];
for (int i = 0; i < V; i++) {
for (int mask = 0; mask < numSubsets; mask++) {
if ((mask & (1 << i)) != 0) {
f[mask] = (f[mask] + f[mask ^ (1 << i)])
% MOD;
}
}
}
// 3. Inclusion-Exclusion
for (int k = 1; k <= V; k++) {
long totalWays = 0;
for (int mask = 0; mask < numSubsets; mask++) {
int setBits = Integer.bitCount(mask);
// Compute f[mask]^k % MOD
long powF = 1;
for (int p = 0; p < k; p++) {
powF = (powF * f[mask]) % MOD;
}
if ((V - setBits) % 2 == 1) {
totalWays
= (totalWays - powF + MOD) % MOD;
}
else {
totalWays = (totalWays + powF) % MOD;
}
}
if (totalWays != 0)
return k;
}
return V;
}
public static void main(String[] args)
{
int V = 7;
int[][] edges
= { { 0, 1 }, { 0, 3 }, { 0, 4 }, { 0, 6 },
{ 1, 2 }, { 1, 4 }, { 1, 6 }, { 2, 5 },
{ 2, 6 }, { 3, 4 }, { 3, 5 }, { 4, 5 } };
int[] adjMask = new int[V];
for (int[] edge : edges) {
adjMask[edge[0]] |= (1 << edge[1]);
adjMask[edge[1]] |= (1 << edge[0]);
}
System.out.println(
solveChromaticNumber(V, adjMask));
}
}
def solve_chromatic_number(V, adj_mask):
# Base case: A graph with no vertices requires 0 days
if V == 0:
return 0
# Large prime to prevent integer overflow while maintaining correctness
MOD = 10**9 + 7
num_subsets = 1 << V
# 1. PRECOMPUTE INDEPENDENT SETS
# I[mask] will be 1 if the subset of vertices in 'mask' is independent
# (meaning no two vertices in that subset share an edge)
I = [0] * num_subsets
I[0] = 1
for mask in range(1, num_subsets):
# Find the index of the lowest set bit
v = (mask & -mask).bit_length() - 1
prev_mask = mask ^ (1 << v)
# A set is independent if its subset was independent AND
# the new vertex v is not connected to any vertex in that subset
if I[prev_mask] == 1 and not (adj_mask[v] & prev_mask):
I[mask] = 1
# 2. SOS DP (SUM OVER SUBSETS)
# f[mask] counts how many independent sets are contained within the subset 'mask'
# This is a standard DP technique to avoid O(3^V) complexity
f = [val for val in I]
for i in range(V):
for mask in range(num_subsets):
if mask & (1 << i):
f[mask] = (f[mask] + f[mask ^ (1 << i)]) % MOD
# 3. INCLUSION-EXCLUSION PRINCIPLE
# We check k-colorability for k = 1 up to V.
# The first k that yields a non-zero number of valid colorings is the answer.
for k in range(1, V + 1):
total_ways = 0
for mask in range(num_subsets):
# Inclusion-Exclusion sign is determined by the size of the complement set
# Sign = (-1) ^ (V - |mask|)
set_bits = bin(mask).count('1')
# Number of ways to choose k independent sets from the subset 'mask'
pow_f = pow(f[mask], k, MOD)
if (V - set_bits) % 2 == 1:
total_ways = (total_ways - pow_f) % MOD
else:
total_ways = (total_ways + pow_f) % MOD
# If total_ways % MOD is not zero, a valid k-coloring exists
if total_ways % MOD != 0:
return k
return V
# --- MAIN EXECUTION ---
V = 7
edges = [
(0, 1), (0, 3), (0, 4), (0, 6),
(1, 2), (1, 4), (1, 6),
(2, 5), (2, 6),
(3, 4), (3, 5),
(4, 5)
]
# Build adjacency masks: adj_mask[i] is a bitmask of neighbors for vertex i
adj_mask = [0] * V
for u, v in edges:
adj_mask[u] |= (1 << v)
adj_mask[v] |= (1 << u)
# Calculate and print only the final answer
print(solve_chromatic_number(V, adj_mask))
using System;
using System.Collections.Generic;
using System.Numerics;
class GFG
{
public static int SolveChromaticNumber(int V, int[] adjMask)
{
if (V == 0) return 0;
const long MOD = 1000000007L;
int numSubsets = 1 << V;
// 1. PRECOMPUTE INDEPENDENT SETS
// I[mask] = 1 if the vertices in 'mask' have no edges between them
int[] I = new int[numSubsets];
I[0] = 1;
for (int mask = 1; mask < numSubsets; mask++)
{
// Bitmask trick to find the index of the lowest set bit
int v = BitOperations.TrailingZeroCount(mask);
int prevMask = mask ^ (1 << v);
// Current mask is independent if its subset was independent
// AND vertex v is not connected to any other vertex in the subset
if (I[prevMask] == 1 && (adjMask[v] & prevMask) == 0)
{
I[mask] = 1;
}
}
// 2. SOS DP (SUM OVER SUBSETS)
// f[mask] counts how many independent sets are contained within the subset 'mask'
long[] f = new long[numSubsets];
for (int mask = 0; mask < numSubsets; mask++) f[mask] = I[mask];
for (int i = 0; i < V; i++)
{
for (int mask = 0; mask < numSubsets; mask++)
{
if ((mask & (1 << i)) != 0)
{
f[mask] = (f[mask] + f[mask ^ (1 << i)]) % MOD;
}
}
}
// 3. INCLUSION-EXCLUSION
// We check k-colorability starting from k=1. The first success is the minimum.
for (int k = 1; k <= V; k++)
{
long totalWays = 0;
for (int mask = 0; mask < numSubsets; mask++)
{
int setBits = BitOperations.PopCount((uint)mask);
// Compute f[mask]^k % MOD
long powF = 1;
for (int p = 0; p < k; p++)
{
powF = (powF * f[mask]) % MOD;
}
// Sign: (-1)^(V - |mask|)
if ((V - setBits) % 2 == 1)
{
totalWays = (totalWays - powF + MOD) % MOD;
}
else
{
totalWays = (totalWays + powF) % MOD;
}
}
// If totalWays is not zero, a valid k-coloring exists
if (totalWays != 0) return k;
}
return V;
}
static void Main()
{
int V = 7;
int[][] edges = {
new[] {0, 1}, new[] {0, 3}, new[] {0, 4}, new[] {0, 6},
new[] {1, 2}, new[] {1, 4}, new[] {1, 6},
new[] {2, 5}, new[] {2, 6},
new[] {3, 4}, new[] {3, 5},
new[] {4, 5}
};
// Construct Adjacency Mask
int[] adjMask = new int[V];
foreach (var edge in edges)
{
adjMask[edge[0]] |= (1 << edge[1]);
adjMask[edge[1]] |= (1 << edge[0]);
}
// Output the absolute minimum
Console.WriteLine(SolveChromaticNumber(V, adjMask));
}
}
function solveChromaticNumber(V, adjMask)
{
// Base case: A graph with no vertices requires 0 days
if (V === 0) {
return 0;
}
// Large prime to prevent integer overflow while
// maintaining correctness
const MOD = 10 ** 9 + 7;
const numSubsets = 1 << V;
// 1. PRECOMPUTE INDEPENDENT SETS
// I[mask] will be 1 if the subset of vertices in'mask'
// is independent (meaning no two vertices in that
// subset share an edge)
const I = Array(numSubsets).fill(0);
I[0] = 1;
for (let mask = 1; mask < numSubsets; mask++) {
// Find the index of the lowest set bit
let v = (mask & -mask).toString(2).length - 1;
const prevMask = mask ^ (1 << v);
// A set is independent if its subset was
// independent AND the new vertex v is not connected
// to any vertex in that subset
if (I[prevMask] === 1 && !(adjMask[v] & prevMask)) {
I[mask] = 1;
}
}
// 2. SOS DP (SUM OVER SUBSETS)
// f[mask] counts how many independent sets are
// contained within the subset'mask' This is a standard
// DP technique to avoid O(3^V) complexity
const f = [...I ];
for (let i = 0; i < V; i++) {
for (let mask = 0; mask < numSubsets; mask++) {
if (mask & (1 << i)) {
f[mask]
= (f[mask] + f[mask ^ (1 << i)]) % MOD;
}
}
}
// 3. INCLUSION-EXCLUSION PRINCIPLE
// We check k-colorability for k = 1 up to V.
// The first k that yields a non-zero number of valid
// colorings is the answer.
for (let k = 1; k <= V; k++) {
let totalWays = 0;
for (let mask = 0; mask < numSubsets; mask++) {
// Inclusion-Exclusion sign is determined by the
// size of the complement set Sign = (-1) ^ (V -
// |mask|)
let setBits
= mask.toString(2).match(/1/g)?.length || 0;
// Number of ways to choose k independent sets
// from the subset'mask'
let powF = Math.pow(f[mask], k) % MOD;
if ((V - setBits) % 2 === 1) {
totalWays = (totalWays - powF + MOD) % MOD;
}
else {
totalWays = (totalWays + powF) % MOD;
}
}
// If totalWays % MOD is not zero, a valid
// k-coloring exists
if (totalWays % MOD !== 0) {
return k;
}
}
return V;
}
// --- MAIN EXECUTION ---
const V = 7;
const edges = [
[ 0, 1 ], [ 0, 3 ], [ 0, 4 ], [ 0, 6 ], [ 1, 2 ],
[ 1, 4 ], [ 1, 6 ], [ 2, 5 ], [ 2, 6 ], [ 3, 4 ],
[ 3, 5 ], [ 4, 5 ]
];
// Build adjacency masks: adjMask[i] is a bitmask of
// neighbors for vertex i
const adjMask = Array(V).fill(0);
for (const [u, v] of edges) {
adjMask[u] |= (1 << v);
adjMask[v] |= (1 << u);
}
// Calculate and print only the final answer
console.log(solveChromaticNumber(V, adjMask));
Output
4
Time Complexity: O(2^N + N)Â
Auxiliary Space: O(2^N)
