Job Assignment Problem using Branch And Bound
Last Updated :
23 Jul, 2025
You are the head of a company with n employees and n distinct jobs to be completed. Every employee takes a different amount of time to complete different jobs, given in the form of a cost[][] matrix, where cost[i][j] represents the time taken by the ith person to complete the jth job. Your task is to assign the jobs in such a way that the total time taken by all employees is minimized.
Note: Each employee must be assigned exactly one job, and each job must be assigned to exactly one employee.
Examples:
Input: n = 4
cost[][] = [ [9, 2, 7, 8],
[6, 4, 3, 7],
[5, 8, 1, 8],
[7, 6, 9, 4] ]
Output: 13
Explanation: Following image depicts the cost associated with each job-worker combinations. Numbers marked in green should be opted to get minimum possible cost.
[Naive Approach] - Generate All Combinations - O(n!) Time and O(1) Space
The idea is to generate n! possible job assignments and for each such assignment, we compute its total cost and return the less expensive assignment. Since the solution is a permutation of the n jobs, its complexity is O(n!).
[Better Approach] - Using Hungarian Algorithm - O(n ^ 3) Time and O(n ^ 2) Space
The optimal assignment can be found using the Hungarian algorithm. The Hungarian algorithm has worst case run-time complexity of O(n^3). We've already discussed the approach in article Hungarian Algorithm for Assignment Problem
[Better Approach] - Using DFS/BFS on State Space Tree
A state space tree is an N-ary tree where each path from the root to a leaf node represents a potential solution to the given problem. One way to explore this tree is through Depth-First Search (DFS), which follows the leftmost path from the root. However, DFS doesn't consider whether each move brings us closer to the goal—successive steps might actually lead us further away. As a result, it’s possible that a valid solution may never be found.
Alternatively, we can use Breadth-First Search (BFS), which explores the tree level by level. Like DFS, BFS also follows a fixed order of exploring nodes regardless of the initial state. Both strategies are blind to the actual direction of the goal unless guided by additional heuristics.
[Expected Approach] - Using Branch Bound - O(n ^ 2) Time and O(n) Space
In both Breadth-First Search (BFS) and Depth-First Search (DFS), the selection of the next node to explore is blind—that is, it does not prioritize nodes that are more likely to lead to an optimal solution. These uninformed strategies treat all nodes equally, without considering their potential to reach the goal efficiently.
To overcome this limitation, we can use an informed search strategy that leverages an intelligent ranking function, often referred to as an approximate cost function. This function helps guide the search process by assigning a cost to each live node, enabling the algorithm to avoid exploring subtrees unlikely to contain optimal solutions. This approach is similar to BFS, but instead of following the strict FIFO order, it selects the live node with the lowest estimated cost.
Although this method does not guarantee the optimal solution, it significantly increases the chances of finding a near-optimal solution faster by focusing the search on more promising paths.
There are two common approaches to estimate the cost function:
- Row-based Minimization: For each worker (row), select the minimum cost job from the list of unassigned jobs (i.e., take the minimum entry from each row).
- Column-based Minimization: For each job (column), choose the worker with the lowest cost from the list of unassigned workers (i.e., take the minimum entry from each column).
In this article, the first approach is followed.
Let’s take below example and try to calculate promising cost when Job 2 is assigned to worker A.

- Since Job 2 is assigned to worker A (marked in green), cost becomes 2 and Job 2 and worker A becomes unavailable (marked in red).

- Now we assign job 3 to worker B as it has minimum cost from list of unassigned jobs. Cost becomes 2 + 3 = 5 and Job 3 and worker B also becomes unavailable.

- Finally, job 1 gets assigned to worker C as it has minimum cost among unassigned jobs and job 4 gets assigned to worker D as it is only Job left. Total cost becomes 2 + 3 + 5 + 4 = 14.

Below diagram shows complete search space diagram showing optimal solution path in green:

Below given is the step-by-step approach:
- Create a dummy root node using new Node(-1, -1, assigned, NULL) with assigned set to false for all jobs, and set root->pathCost and root->cost to 0.
- Insert the root node into a min-heap priority queue (live nodes) ordered by node->cost.
- While the priority queue is not empty:
- Extract node E using Least() (i.e. pq.top() then pq.pop()).
- Let worker = E->workerID + 1.
- If worker equals N (all workers assigned), use printAssignments(E) to print the job assignment and return E->cost.
- For each job j from 0 to N-1:
- If E->assigned[j] is false:
- Create child node x = new Node(worker, j, E->assigned, E).
- Set x->pathCost = E->pathCost + costMatrix[worker][j].
- Calculate lower bound using calculateCost(costMatrix, worker, j, x->assigned) and set x->cost = x->pathCost + lower bound.
- Insert x into the priority queue using Add(x).
C++
#include <bits/stdc++.h>
using namespace std;
// state space tree node
class Node {
public:
// stores parent node of current node
// helps in tracing path when answer is found
Node *parent;
// contains cost for ancestors nodes
// including current node
int pathCost;
// contains least promising cost
int cost;
// contain worker number
int workerID;
// contains Job ID
int jobID;
// Boolean array assigned will contains
// info about available jobs
vector<bool> assigned;
Node(int x, int y, vector<bool> assigned, Node *parent) {
this->workerID = x;
this->jobID = y;
this->assigned = assigned;
this->parent = parent;
this->pathCost = 0;
this->cost = 0;
}
};
// Function to calculate the least promising cost
// of node after worker x is assigned to job y.
int calculateCost(vector<vector<int>> &costMat, int x, int y, vector<bool> &assigned) {
int n = costMat.size();
int cost = 0;
// to store unavailable jobs
vector<bool> available(n, true);
// start from next worker
for (int i = x + 1; i < n; i++) {
int min = INT_MAX, minIndex = -1;
// do for each job
for (int j = 0; j < n; j++) {
// if job is unassigned
if (!assigned[j] && available[j] && costMat[i][j] < min) {
// store job number
minIndex = j;
// store cost
min = costMat[i][j];
}
}
// add cost of next worker
cost += min;
// job becomes unavailable
available[minIndex] = false;
}
return cost;
}
// Comparison object to be used to order the heap
struct comp {
bool operator()(const Node *lhs, const Node *rhs) const {
return lhs->cost > rhs->cost;
}
};
// Finds minimum cost using Branch and Bound.
int findMinCost(vector<vector<int>> &costMat) {
int n = costMat.size();
// Create a priority queue to store
// live nodes of search tree;
priority_queue<Node *, vector<Node *>, comp> pq;
// initialize heap to dummy node with cost 0
vector<bool> assigned(n, false);
Node *root = new Node(-1, -1, assigned, nullptr);
root->pathCost = root->cost = 0;
root->workerID = -1;
// Add dummy node to list of live nodes;
pq.push(root);
while (!pq.empty()) {
// Find a live node with least estimated cost
Node *min = pq.top();
// The found node is deleted from the list
pq.pop();
// i stores next worker
int i = min->workerID + 1;
// if all workers are assigned a job
if (i == n) {
return min->cost;
}
// do for each job
for (int j = 0; j < n; j++) {
// If unassigned
if (!min->assigned[j]) {
// create a new tree node
Node *child = new Node(i, j, min->assigned, min);
child->assigned[j] = true;
// cost for ancestors nodes including current node
child->pathCost = min->pathCost + costMat[i][j];
// calculate its lower bound
child->cost = child->pathCost + calculateCost(costMat, i, j, child->assigned);
// Add child to list of live nodes;
pq.push(child);
}
}
}
// will not be used
return -1;
}
int main() {
vector<vector<int>> costMat = {
{9, 2, 7, 8},
{6, 4, 3, 7},
{5, 8, 1, 8},
{7, 6, 9, 4}
};
cout << findMinCost(costMat);
return 0;
}
Java
import java.util.*;
public class GfG {
// state space tree node
static class Node {
// stores parent node of current node
// helps in tracing path when answer is found
Node parent;
// contains cost for ancestors nodes
// including current node
int pathCost;
// contains least promising cost
int cost;
// contain worker number
int workerID;
// contains Job ID
int jobID;
// Boolean array assigned will contains
// info about available jobs
boolean[] assigned;
Node(int x, int y, boolean[] assigned, Node parent) {
this.workerID = x;
this.jobID = y;
this.assigned = assigned.clone();
this.parent = parent;
this.pathCost = 0;
this.cost = 0;
}
}
// Function to calculate the least promising cost
// of node after worker x is assigned to job y.
static int calculateCost(int[][] costMat,
int x, int y, boolean[] assigned) {
int n = costMat.length;
int cost = 0;
// to store unavailable jobs
boolean[] available = new boolean[n];
Arrays.fill(available, true);
// start from next worker
for (int i = x + 1; i < n; i++) {
int min = Integer.MAX_VALUE, minIndex = -1;
// do for each job
for (int j = 0; j < n; j++) {
// if job is unassigned
if (!assigned[j] && available[j] && costMat[i][j] < min) {
// store job number
minIndex = j;
// store cost
min = costMat[i][j];
}
}
// add cost of next worker
cost += min;
// job becomes unavailable
available[minIndex] = false;
}
return cost;
}
// Comparison object to be used to order the heap
static class Comp implements Comparator<Node> {
public int compare(Node lhs, Node rhs) {
return lhs.cost - rhs.cost;
}
}
// Finds minimum cost using Branch and Bound.
static int findMinCost(int[][] costMat) {
int n = costMat.length;
// Create a priority queue to store
// live nodes of search tree;
PriorityQueue<Node> pq = new PriorityQueue<>(new Comp());
// initialize heap to dummy node with cost 0
boolean[] assigned = new boolean[n];
Node root = new Node(-1, -1, assigned, null);
root.pathCost = root.cost = 0;
root.workerID = -1;
// Add dummy node to list of live nodes;
pq.add(root);
while (!pq.isEmpty()) {
// Find a live node with least estimated cost
Node min = pq.poll();
// The found node is deleted from the list
// i stores next worker
int i = min.workerID + 1;
// if all workers are assigned a job
if (i == n) {
return min.cost;
}
// do for each job
for (int j = 0; j < n; j++) {
// If unassigned
if (!min.assigned[j]) {
// create a new tree node
Node child = new Node(i, j, min.assigned, min);
child.assigned[j] = true;
// cost for ancestors nodes including current node
child.pathCost = min.pathCost + costMat[i][j];
// calculate its lower bound
child.cost = child.pathCost +
calculateCost(costMat, i, j, child.assigned);
// Add child to list of live nodes;
pq.add(child);
}
}
}
// will not be used
return -1;
}
public static void main(String[] args) {
int[][] costMat = {
{9, 2, 7, 8},
{6, 4, 3, 7},
{5, 8, 1, 8},
{7, 6, 9, 4}
};
System.out.println(findMinCost(costMat));
}
}
Python
import heapq
import math
# state space tree node
class Node:
# stores parent node of current node
# helps in tracing path when answer is found
def __init__(self, x, y, assigned, parent):
self.workerID = x
self.jobID = y
self.assigned = assigned[:] # copy list
self.parent = parent
self.pathCost = 0
self.cost = 0
def __lt__(self, other):
return self.cost < other.cost
# Function to calculate the least promising cost
# of node after worker x is assigned to job y.
def calculateCost(costMat, x, y, assigned):
n = len(costMat)
cost = 0
# to store unavailable jobs
available = [True] * n
# start from next worker
for i in range(x + 1, n):
minVal = float('inf')
minIndex = -1
# do for each job
for j in range(n):
# if job is unassigned
if (not assigned[j]) and available[j] and costMat[i][j] < minVal:
# store job number
minIndex = j
# store cost
minVal = costMat[i][j]
# add cost of next worker
cost += minVal
# job becomes unavailable
available[minIndex] = False
return cost
# Finds minimum cost using Branch and Bound.
def findMinCost(costMat):
n = len(costMat)
# Create a priority queue to store
# live nodes of search tree;
pq = []
# initialize heap to dummy node with cost 0
assigned = [False] * n
root = Node(-1, -1, assigned, None)
root.pathCost = root.cost = 0
root.workerID = -1
# Add dummy node to list of live nodes;
heapq.heappush(pq, root)
while pq:
# Find a live node with least estimated cost
minNode = heapq.heappop(pq)
# The found node is deleted from the list
# i stores next worker
i = minNode.workerID + 1
# if all workers are assigned a job
if i == n:
return minNode.cost
# do for each job
for j in range(n):
# If unassigned
if not minNode.assigned[j]:
# create a new tree node
child = Node(i, j, minNode.assigned, minNode)
child.assigned[j] = True
# cost for ancestors nodes including current node
child.pathCost = minNode.pathCost + costMat[i][j]
# calculate its lower bound
child.cost = child.pathCost + calculateCost(costMat, i, j, child.assigned)
# Add child to list of live nodes;
heapq.heappush(pq, child)
return -1
if __name__ == "__main__":
costMat = [
[9, 2, 7, 8],
[6, 4, 3, 7],
[5, 8, 1, 8],
[7, 6, 9, 4]
]
print(findMinCost(costMat))
C#
using System;
using System.Collections.Generic;
public class GfG {
// state space tree node
public class Node {
// stores parent node of current node
// helps in tracing path when answer is found
public Node parent;
// contains cost for ancestors nodes
// including current node
public int pathCost;
// contains least promising cost
public int cost;
// contain worker number
public int workerID;
// contains Job ID
public int jobID;
// Boolean array assigned will contains
// info about available jobs
public bool[] assigned;
public Node(int x, int y, bool[] assigned, Node parent) {
this.workerID = x;
this.jobID = y;
this.assigned = (bool[])assigned.Clone();
this.parent = parent;
this.pathCost = 0;
this.cost = 0;
}
}
// Function to calculate the least promising cost
// of node after worker x is assigned to job y.
public static int calculateCost(
int[][] costMat, int x, int y, bool[] assigned) {
int n = costMat.Length;
int cost = 0;
// to store unavailable jobs
bool[] available = new bool[n];
for (int i = 0; i < n; i++) {
available[i] = true;
}
// start from next worker
for (int i = x + 1; i < n; i++) {
int min = int.MaxValue, minIndex = -1;
// do for each job
for (int j = 0; j < n; j++) {
// if job is unassigned
if (!assigned[j] && available[j] && costMat[i][j] < min) {
// store job number
minIndex = j;
// store cost
min = costMat[i][j];
}
}
// add cost of next worker
cost += min;
// job becomes unavailable
available[minIndex] = false;
}
return cost;
}
// Comparison object to be used to order the heap
public class NodeComparer : IComparer<Node> {
public int Compare(Node lhs, Node rhs) {
return lhs.cost.CompareTo(rhs.cost);
}
}
// Finds minimum cost using Branch and Bound.
public static int findMinCost(int[][] costMat) {
int n = costMat.Length;
// Create a priority queue to store
// live nodes of search tree;
SortedSet<Node> pq =
new SortedSet<Node>(new NodeComparer());
// initialize heap to dummy node with cost 0
bool[] assigned = new bool[n];
Node root = new Node(-1, -1, assigned, null);
root.pathCost = root.cost = 0;
root.workerID = -1;
// Add dummy node to list of live nodes;
pq.Add(root);
while (pq.Count > 0) {
// Find a live node with least estimated cost
Node min = pq.Min;
pq.Remove(min);
// The found node is deleted from the list
// i stores next worker
int i = min.workerID + 1;
// if all workers are assigned a job
if (i == n) {
return min.cost;
}
// do for each job
for (int j = 0; j < n; j++) {
// If unassigned
if (!min.assigned[j]) {
// create a new tree node
Node child = new Node(i, j, min.assigned, min);
child.assigned[j] = true;
// cost for ancestors nodes including current node
child.pathCost = min.pathCost + costMat[i][j];
// calculate its lower bound
child.cost = child.pathCost +
calculateCost(costMat, i, j, child.assigned);
// Add child to list of live nodes;
pq.Add(child);
}
}
}
// will not be used
return -1;
}
public static void Main(string[] args) {
int[][] costMat = new int[][] {
new int[] {9, 2, 7, 8},
new int[] {6, 4, 3, 7},
new int[] {5, 8, 1, 8},
new int[] {7, 6, 9, 4}
};
Console.WriteLine(findMinCost(costMat));
}
}
JavaScript
// state space tree node
class Node {
// stores parent node of current node
// helps in tracing path when answer is found
constructor(x, y, assigned, parent) {
this.workerID = x;
this.jobID = y;
this.assigned = assigned.slice();
this.parent = parent;
this.pathCost = 0;
this.cost = 0;
}
}
// Function to calculate the least promising cost
// of node after worker x is assigned to job y.
function calculateCost(costMat, x, y, assigned) {
let n = costMat.length;
let cost = 0;
// to store unavailable jobs
let available = new Array(n).fill(true);
// start from next worker
for (let i = x + 1; i < n; i++) {
let min = Infinity, minIndex = -1;
// do for each job
for (let j = 0; j < n; j++) {
// if job is unassigned
if (!assigned[j] && available[j] && costMat[i][j] < min) {
// store job number
minIndex = j;
// store cost
min = costMat[i][j];
}
}
// add cost of next worker
cost += min;
// job becomes unavailable
available[minIndex] = false;
}
return cost;
}
// Comparison function to be used to order the heap
function compareNodes(lhs, rhs) {
return lhs.cost - rhs.cost;
}
// Finds minimum cost using Branch and Bound.
function findMinCost(costMat) {
let n = costMat.length;
// Create a priority queue to store
// live nodes of search tree;
let pq = [];
// initialize heap to dummy node with cost 0
let assigned = new Array(n).fill(false);
let root = new Node(-1, -1, assigned, null);
root.pathCost = root.cost = 0;
root.workerID = -1;
// Add dummy node to list of live nodes;
pq.push(root);
while (pq.length > 0) {
// Find a live node with least estimated cost
pq.sort(compareNodes);
let min = pq.shift();
// The found node is deleted from the list
// i stores next worker
let i = min.workerID + 1;
// if all workers are assigned a job
if (i === n) {
return min.cost;
}
// do for each job
for (let j = 0; j < n; j++) {
// If unassigned
if (!min.assigned[j]) {
// create a new tree node
let child = new Node(i, j, min.assigned, min);
child.assigned[j] = true;
// cost for ancestors nodes including current node
child.pathCost = min.pathCost + costMat[i][j];
// calculate its lower bound
child.cost = child.pathCost +
calculateCost(costMat, i, j, child.assigned);
// Add child to list of live nodes;
pq.push(child);
}
}
}
// will not be used
return -1;
}
let costMat = [
[9, 2, 7, 8],
[6, 4, 3, 7],
[5, 8, 1, 8],
[7, 6, 9, 4]
];
console.log(findMinCost(costMat));
Job assignment Problem | DSA Problem
Similar Reads
Basics & Prerequisites
Data Structures
Array Data StructureIn this article, we introduce array, implementation in different popular languages, its basic operations and commonly seen problems / interview questions. An array stores items (in case of C/C++ and Java Primitive Arrays) or their references (in case of Python, JS, Java Non-Primitive) at contiguous
3 min read
String in Data StructureA string is a sequence of characters. The following facts make string an interesting data structure.Small set of elements. Unlike normal array, strings typically have smaller set of items. For example, lowercase English alphabet has only 26 characters. ASCII has only 256 characters.Strings are immut
2 min read
Hashing in Data StructureHashing is a technique used in data structures that efficiently stores and retrieves data in a way that allows for quick access. Hashing involves mapping data to a specific index in a hash table (an array of items) using a hash function. It enables fast retrieval of information based on its key. The
2 min read
Linked List Data StructureA linked list is a fundamental data structure in computer science. It mainly allows efficient insertion and deletion operations compared to arrays. Like arrays, it is also used to implement other data structures like stack, queue and deque. Hereâs the comparison of Linked List vs Arrays Linked List:
2 min read
Stack Data StructureA Stack is a linear data structure that follows a particular order in which the operations are performed. The order may be LIFO(Last In First Out) or FILO(First In Last Out). LIFO implies that the element that is inserted last, comes out first and FILO implies that the element that is inserted first
2 min read
Queue Data StructureA Queue Data Structure is a fundamental concept in computer science used for storing and managing data in a specific order. It follows the principle of "First in, First out" (FIFO), where the first element added to the queue is the first one to be removed. It is used as a buffer in computer systems
2 min read
Tree Data StructureTree Data Structure is a non-linear data structure in which a collection of elements known as nodes are connected to each other via edges such that there exists exactly one path between any two nodes. Types of TreeBinary Tree : Every node has at most two childrenTernary Tree : Every node has at most
4 min read
Graph Data StructureGraph Data Structure is a collection of nodes connected by edges. It's used to represent relationships between different entities. If you are looking for topic-wise list of problems on different topics like DFS, BFS, Topological Sort, Shortest Path, etc., please refer to Graph Algorithms. Basics of
3 min read
Trie Data StructureThe Trie data structure is a tree-like structure used for storing a dynamic set of strings. It allows for efficient retrieval and storage of keys, making it highly effective in handling large datasets. Trie supports operations such as insertion, search, deletion of keys, and prefix searches. In this
15+ min read
Algorithms
Searching AlgorithmsSearching algorithms are essential tools in computer science used to locate specific items within a collection of data. In this tutorial, we are mainly going to focus upon searching in an array. When we search an item in an array, there are two most common algorithms used based on the type of input
2 min read
Sorting AlgorithmsA Sorting Algorithm is used to rearrange a given array or list of elements in an order. For example, a given array [10, 20, 5, 2] becomes [2, 5, 10, 20] after sorting in increasing order and becomes [20, 10, 5, 2] after sorting in decreasing order. There exist different sorting algorithms for differ
3 min read
Introduction to RecursionThe process in which a function calls itself directly or indirectly is called recursion and the corresponding function is called a recursive function. A recursive algorithm takes one step toward solution and then recursively call itself to further move. The algorithm stops once we reach the solution
14 min read
Greedy AlgorithmsGreedy algorithms are a class of algorithms that make locally optimal choices at each step with the hope of finding a global optimum solution. At every step of the algorithm, we make a choice that looks the best at the moment. To make the choice, we sometimes sort the array so that we can always get
3 min read
Graph AlgorithmsGraph is a non-linear data structure like tree data structure. The limitation of tree is, it can only represent hierarchical data. For situations where nodes or vertices are randomly connected with each other other, we use Graph. Example situations where we use graph data structure are, a social net
3 min read
Dynamic Programming or DPDynamic Programming is an algorithmic technique with the following properties.It is mainly an optimization over plain recursion. Wherever we see a recursive solution that has repeated calls for the same inputs, we can optimize it using Dynamic Programming. The idea is to simply store the results of
3 min read
Bitwise AlgorithmsBitwise algorithms in Data Structures and Algorithms (DSA) involve manipulating individual bits of binary representations of numbers to perform operations efficiently. These algorithms utilize bitwise operators like AND, OR, XOR, NOT, Left Shift, and Right Shift.BasicsIntroduction to Bitwise Algorit
4 min read
Advanced
Segment TreeSegment Tree is a data structure that allows efficient querying and updating of intervals or segments of an array. It is particularly useful for problems involving range queries, such as finding the sum, minimum, maximum, or any other operation over a specific range of elements in an array. The tree
3 min read
Pattern SearchingPattern searching algorithms are essential tools in computer science and data processing. These algorithms are designed to efficiently find a particular pattern within a larger set of data. Patten SearchingImportant Pattern Searching Algorithms:Naive String Matching : A Simple Algorithm that works i
2 min read
GeometryGeometry is a branch of mathematics that studies the properties, measurements, and relationships of points, lines, angles, surfaces, and solids. From basic lines and angles to complex structures, it helps us understand the world around us.Geometry for Students and BeginnersThis section covers key br
2 min read
Interview Preparation
Practice Problem