Minimum number of days required to schedule all exams

Last Updated : 3 Feb, 2026

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:

C++
#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;
}
Java
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));
    }
}
Python
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))
C#
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));
    }
}
JavaScript
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)

Comment