Maximum edges that can be added to DAG so that it remains DAG
Last Updated :
28 Feb, 2023
A DAG is given to us, we need to find maximum number of edges that can be added to this DAG, after which new graph still remain a DAG that means the reformed graph should have maximal number of edges, adding even single edge will create a cycle in graph.
The Output for above example should be following edges in any order.
4-2, 4-5, 4-3, 5-3, 5-1, 2-0, 2-1, 0-3, 0-1
As shown in above example, we have added all the edges in one direction only to save ourselves from making a cycle. This is the trick to solve this question. We sort all our nodes in topological order and create edges from node to all nodes to the right if not there already.
How can we say that, it is not possible to add any more edge? the reason is we have added all possible edges from left to right and if we want to add more edge we need to make that from right to left, but adding edge from right to left we surely create a cycle because its counter part left to right edge is already been added to graph and creating cycle is not what we needed.
So solution proceeds as follows, we consider the nodes in topological order and if any edge is not there from left to right, we will create it.
Below is the solution, we have printed all the edges that can be added to given DAG without making any cycle.
C++
#include <bits/stdc++.h>
using namespace std;
class Graph {
int V;
list< int >* adj;
vector< int > indegree;
vector< int > topologicalSort();
public :
Graph( int V);
void addEdge( int v, int w);
void maximumEdgeAddition();
};
Graph::Graph( int V)
{
this ->V = V;
adj = new list< int >[V];
for ( int i = 0; i < V; i++)
indegree.push_back(0);
}
void Graph::addEdge( int v, int w)
{
adj[v].push_back(w);
indegree[w]++;
}
vector< int > Graph::topologicalSort()
{
vector< int > topological;
queue< int > q;
for ( int i = 0; i < V; i++)
if (indegree[i] == 0)
q.push(i);
while (!q.empty()) {
int t = q.front();
q.pop();
topological.push_back(t);
for (list< int >::iterator j = adj[t].begin();
j != adj[t].end(); j++) {
indegree[*j]--;
if (indegree[*j] == 0)
q.push(*j);
}
}
return topological;
}
void Graph::maximumEdgeAddition()
{
bool * visited = new bool [V];
vector< int > topo = topologicalSort();
for ( int i = 0; i < topo.size(); i++) {
int t = topo[i];
for (list< int >::iterator j = adj[t].begin();
j != adj[t].end(); j++)
visited[*j] = true ;
for ( int j = i + 1; j < topo.size(); j++) {
if (!visited[topo[j]])
cout << t << "-" << topo[j] << " " ;
visited[topo[j]] = false ;
}
}
}
int main()
{
Graph g(6);
g.addEdge(5, 2);
g.addEdge(5, 0);
g.addEdge(4, 0);
g.addEdge(4, 1);
g.addEdge(2, 3);
g.addEdge(3, 1);
g.maximumEdgeAddition();
return 0;
}
|
Java
import java.util.*;
public class Graph {
int V;
ArrayList<ArrayList<Integer>> adj;
int [] indegree;
Graph( int v)
{
this .V = v;
indegree = new int [V];
adj = new ArrayList<>(V);
for ( int i = 0 ; i < V; i++)
{
adj.add( new ArrayList<Integer>());
indegree[i] = 0 ;
}
}
public void addEdge( int v, int w)
{
adj.get(v).add(w);
indegree[w]++;
}
public List<Integer> topologicalSort()
{
List<Integer> topological = new ArrayList<>(V);
Queue<Integer> q = new LinkedList<>();
for ( int i = 0 ; i < V; i++)
{
if (indegree[i] == 0 )
{
q.add(i);
}
}
while (!q.isEmpty())
{
int t=q.poll();
topological.add(t);
for ( int j: adj.get(t))
{
indegree[j]--;
if (indegree[j] == 0 )
{
q.add(j);
}
}
}
return topological;
}
public void maximumEdgeAddition()
{
boolean [] visited = new boolean [V];
List<Integer> topo=topologicalSort();
for ( int i = 0 ; i < topo.size(); i++)
{
int t = topo.get(i);
for ( Iterator<Integer> j = adj.get(t).listIterator();j.hasNext();)
{
visited[j.next()] = true ;
}
for ( int j = i + 1 ; j < topo.size(); j++)
{
if (!visited[topo.get(j)])
{
System.out.print( t + "-" + topo.get(j) + " " );
}
visited[topo.get(j)] = false ;
}
}
}
public static void main(String[] args)
{
Graph g = new Graph( 6 );
g.addEdge( 5 , 2 );
g.addEdge( 5 , 0 );
g.addEdge( 4 , 0 );
g.addEdge( 4 , 1 );
g.addEdge( 2 , 3 );
g.addEdge( 3 , 1 );
g.maximumEdgeAddition();
return ;
}
}
|
Python3
class Graph:
def __init__( self , V):
self .V = V
self .adj = [[] for i in range (V)]
self .indegree = [ 0 for i in range (V)]
def addEdge( self , v, w):
self .adj[v].append(w)
self .indegree[w] + = 1
def topologicalSort( self ):
topological = []
q = []
for i in range ( self .V):
if ( self .indegree[i] = = 0 ):
q.append(i)
while ( len (q) ! = 0 ):
t = q[ 0 ]
q.pop( 0 )
topological.append(t)
for j in self .adj[t]:
self .indegree[j] - = 1
if ( self .indegree[j] = = 0 ):
q.append(j)
return topological
def maximumEdgeAddition( self ):
visited = [ False for i in range ( self .V)]
topo = self .topologicalSort()
for i in range ( len (topo)):
t = topo[i]
for j in self .adj[t]:
visited[j] = True
for j in range (i + 1 , len (topo)):
if ( not visited[topo[j]]):
print ( str (t) + '-' +
str (topo[j]), end = ' ' )
visited[topo[j]] = False
if __name__ = = '__main__' :
g = Graph( 6 )
g.addEdge( 5 , 2 )
g.addEdge( 5 , 0 )
g.addEdge( 4 , 0 )
g.addEdge( 4 , 1 )
g.addEdge( 2 , 3 )
g.addEdge( 3 , 1 )
g.maximumEdgeAddition()
|
C#
using System;
using System.Collections.Generic;
public class Graph {
private int V;
private List< int >[] adj;
private int [] indegree;
public Graph( int v)
{
V = v;
indegree = new int [V];
adj = new List< int >[ V ];
for ( int i = 0; i < V; i++) {
adj[i] = new List< int >();
indegree[i] = 0;
}
}
public void AddEdge( int v, int w)
{
adj[v].Add(w);
indegree[w]++;
}
public List< int > TopologicalSort()
{
List< int > topological = new List< int >();
Queue< int > q = new Queue< int >();
for ( int i = 0; i < V; i++) {
if (indegree[i] == 0) {
q.Enqueue(i);
}
}
while (q.Count > 0) {
int t = q.Dequeue();
topological.Add(t);
foreach ( int j in adj[t])
{
indegree[j]--;
if (indegree[j] == 0) {
q.Enqueue(j);
}
}
}
return topological;
}
public void MaximumEdgeAddition()
{
bool [] visited = new bool [V];
List< int > topo = TopologicalSort();
for ( int i = 0; i < topo.Count; i++) {
int t = topo[i];
foreach ( int j in adj[t]) { visited[j] = true ; }
for ( int j = i + 1; j < topo.Count; j++) {
if (!visited[topo[j]]) {
Console.Write(t + "-" + topo[j] + " " );
}
visited[topo[j]] = false ;
}
}
Console.WriteLine();
}
static void Main( string [] args)
{
Graph g = new Graph(6);
g.AddEdge(5, 2);
g.AddEdge(5, 0);
g.AddEdge(4, 0);
g.AddEdge(4, 1);
g.AddEdge(2, 3);
g.AddEdge(3, 1);
g.MaximumEdgeAddition();
}
}
|
Javascript
class Graph{
constructor(V){
this .V = V
this .adj = new Array(V);
for (let i = 0; i < V; i++){
this .adj[i] = new Array();
}
this .indegree = new Array(V).fill(0);
}
addEdge(v, w){
this .adj[v].push(w)
this .indegree[w] += 1
}
topologicalSort(){
let topological = new Array();
let q = new Array();
for (let i= 0; i < this .V; i++){
if ( this .indegree[i] == 0){
q.push(i);
}
}
while (q.length != 0){
let t = q[0];
q.shift();
topological.push(t)
for (let indx = 0; indx < this .adj[t].length; indx++){
let j = this .adj[t][indx];
this .indegree[j] -= 1;
if ( this .indegree[j] == 0){
q.push(j);
}
}
}
return topological;
}
maximumEdgeAddition(){
let visited = new Array( this .V).fill( false );
let topo = this .topologicalSort();
for (let i = 0; i < topo.length; i++){
let t = topo[i];
for (let indx = 0; indx < this .adj[t].length; indx++){
let j = this .adj[t][indx];
visited[j] = true ;
}
for (let j = i+1; j < topo.length; j++){
if (!visited[topo[j]]){
console.log(t.toString() + '- ' + topo[j].toString() + ' ');
}
visited[topo[j]] = false ;
}
}
}
}
let g = new Graph(6);
g.addEdge(5, 2);
g.addEdge(5, 0);
g.addEdge(4, 0);
g.addEdge(4, 1);
g.addEdge(2, 3);
g.addEdge(3, 1);
g.maximumEdgeAddition();
|
Output
4-5 4-2 4-3 5-3 5-1 2-0 2-1 0-3 0-1
Time complexity : O(V + E)
Space complexity : O(V + E)
Optimized approach for finding only the number of edges that can be added
If we have to find only the maximum number of edges that can be added, it can be solved simply by understanding the following observation. If a DAG has n nodes, it has n nodes in it’s topological sort as well. The maximum number of edges a DAG can have can be determined by finding the maximum number of nodes that can be linked with each node. This implies the first node is connected to (n-1) nodes, the second node is connected to (n-2) nodes until the last node is not connected to any node. The reason for this decreasing pattern is because any edge from right node to the left node of the topological sort creates a cycle and it’ll no longer be a DAG.
This means a DAG of n nodes can have a maximum number of
(n-1) + (n-2) + (n-3) + . . . 3 + 2 + 1 = n*(n-1)/2 edges
If the DAG already has E edges, then the maximum number of additional edges that can be added is given by (n*(n-1)/2 – E) which will be our final answer.
Below is the code implementation of the optimized approach
C++
#include <iostream>
using namespace std;
int max_edges( int V, int E)
{
return V * (V - 1) / 2 - E;
}
int main()
{
int V, E;
V = 6;
E = 6;
cout << "Maximum number of edges that can be added to maintain DAG is " << max_edges(V, E);
return 0;
}
|
Python3
def max_edges(V,E):
return V * (V - 1 ) / / 2 - E
V = 6
E = 6
print ( "Maximum number of edges that can be added to maintain DAG is" ,max_edges(V,E))
|
Java
import java.io.*;
public class Main {
static int max_edges( int V, int E) {
return (V * (V - 1 ) / 2 ) - E;
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader( new InputStreamReader(System.in));
int V, E;
V = 6 ;
E = 6 ;
System.out.println( "Maximum number of edges that can be added to maintain DAG is " + max_edges(V, E));
}
}
|
C#
using System;
class MainClass {
static int MaxEdges( int V, int E) {
return (V * (V - 1) / 2) - E;
}
public static void Main( string [] args) {
int V, E;
V = 6;
E = 6;
Console.WriteLine( "Maximum number of edges that can be added to maintain DAG is " + MaxEdges(V, E));
}
}
|
Javascript
function maxEdges(V, E) {
return (V * (V - 1) / 2) - E;
}
const V = 6;
const E = 6;
console.log(`Maximum number of edges that can be added to maintain DAG is ${maxEdges(V, E)}`);
|
Output
Maximum number of edges that can be added to maintain DAG is 9
Time Complexity: O(1)
Auxiliary Space: O(1)
NOTE: A DAG can have multiple topological sorting orders, but for each case, the maximum number of edges that can be added to maintain DAG remains same.
Please Login to comment...