data structuresproject report-warshall's algorithm

24
NATIONAL INSTITUTE OF TECHNOLOGY TIRUCHIRAPPALLI Department of Electrical and Electronics Engineering PROJECT ON DATA STRUCTURES AND C++

Upload: jklmeenu

Post on 15-Dec-2015

21 views

Category:

Documents


4 download

TRANSCRIPT

NATIONAL INSTITUTE OF TECHNOLOGY

TIRUCHIRAPPALLI

Department of Electrical and Electronics Engineering

PROJECT ON

DATA STRUCTURES AND C++

Vignesh
Pencil
Vignesh
Pencil
Vignesh
Pencil

Page 2 of 24

WARSHALL’S ALGORITHM

PROJECT TEAM:

Bollu Karthik – 107112015

Guru Praanesh R – 107112030

Guru Raghav R – 107112031

V Vignesh Venkatasubramanian – 107112098

Vignesh
Pencil
Vignesh
Pencil

Page 3 of 24

PROBLEM STATEMENT

Given a list of cities and the interconnection between cities, write a program in C++ to

1) Find the path matrix using Warshall’s algorithm.

2) List all possible paths between the given cities.

3) Check for existence of a cycle in the graph.

The input and output are from/to files. The files names are passed as command line

arguments.

Page 4 of 24

THEORY

Warshall’s Algorithm to find Transitive Closure

Let S be the finite set {v1 , ..., v n }, R a relation on S. The adjacency matrix A of R is an n x n

Boolean (zero-one) matrix defined by

Ai,j = 1 if the digraph D has an edge from vi to vj

0 if the digraph D has an edge from vi to vj

Warshall’s algorithm is an efficient method of finding the adjacency matrix of the transitive

closure of relation R on a finite set S from the adjacency matrix of R. It uses properties of the

digraph D, in particular, walks of various lengths in D.

Let A be the adjacency matrix of R and T be the adjacency matrix of the transitive closure of

R. T is called the reachability matrix of digraph D due to the property that T i, j = 1 if and only

if vj can be reached from vi in D by a sequence of arcs (edges).

In Warshall’s algorithm we construct a sequence of Boolean matrices A = W[0] , W[1] , W[2] , ...,

W[n] =T, where A and T are as above. This can be done from digraph D as follows.

[W[1]] i, j = 1 if and only if there is a walk from vi to vj with elements of a subset of {v1 }as

interior vertices.

[W[2]] i, j = 1 if and only if there is a walk from vi to vj with elements of a subset of {v1,v2}as

interior vertices.

Continuing this process, we generalize to:

[W[k]] i, j = 1 if and only if there is a walk from vi to vj with elements of a subset of { v1, v 2 , ...

, v k } as interior vertices.

In constructing W[k ] from W[k −1] we either keep zeros or change some zeros to ones. No ones

ever get changed to zeros.

Generation of Transitive Closure (Path matrix)

The recurrence relating elements R(k) to elements of R(k-1) is:

R(k)[i,j] = R(k-1)[i,j] OR (R(k-1)[i,k] AND R(k-1)[k,j])

It implies the following rules for generating R(k) from R(k-1):

Rule 1: If an element in row i and column j is 1 in R(k-1), it remains 1 in R(k).

Rule 2: If an element in row i and column j is 0 in R(k-1), it has to be changed to 1 in R(k) if

and only if the element in its row i and column k and the element in its column j and row k

are both 1’s in R(k-1).

Page 5 of 24

WARSHALL’S ALGORITHM

Input: Adjacency matrix A of relation R on a set of n elements

Output: Adjacency matrix T of the transitive closure of R.

T := A [initialize T to A]

for j := 1 to n

for i := 1 to n

if i j T , = 1 then

i a := i j a ∨ a [form the Boolean OR of row i and row j, store it in i a ]

next i

next j

END

Time efficiency: 𝜃(n3)

Space efficiency: Matrices can be written over their predecessors

File Handling in C++

C++ provides the following classes to perform output and input of characters to/from files:

ofstream: Stream class to write on files

ifstream: Stream class to read from files

fstream: Stream class to both read and write from/to files.

These classes are derived directly or indirectly from the classes istream and ostream. The first

operation generally performed on an object of one of these classes is to associate it to a real

file. This procedure is known as to open a file. An open file is represented within a program

by a stream and any input or output operation performed on this stream object will be applied

to the physical file associated to it. In order to open a file with a stream object we use its

member function open:

filename.open (filename, mode);

Here filename is a string representing the name of the file to be opened, and mode is an

optional parameter with a combination of the following flags:

ios::in Open for input operations.

ios::out Open for output operations.

ios::binary Open in binary mode.

ios::ate Set the initial position at the end of the file.

If this flag is not set, the initial position is the beginning of the file.

ios::app All output operations are performed at the end of the file, appending the content

to the current content of the file.

ios::trunc If the file is opened for output operations and it already existed, its previous

content is deleted and replaced by the new one.

All these flags can be combined using the bitwise operator OR (|). For example, if we want to

Page 6 of 24

open the file example.bin in binary mode to add data we could do it by the following call to

member function open:

ofstream myfile;

myfile.open ("example.bin", ios::out | ios::app | ios::binary);

Each of the open member functions of classes ofstream, ifstream and fstream has a default

mode that is used if the file is opened without a second argument:

CLASS DEFAULT MODE PARAMETER

ofstream ios::out

ifstream ios::in

fstream ios::in | ios::out

For ifstream and ofstream classes, ios::in and ios::out are automatically and respectively

assumed, even if a mode that does not include them is passed as second argument to

the open member function (the flags are combined).

For fstream, the default value is only applied if the function is called without specifying any

value for the mode parameter. If the function is called with any value in that parameter the

default mode is overridden, not combined.

File streams opened in binary mode perform input and output operations independently of

any format considerations. Non-binary files are known as text files, and some translations

may occur due to formatting of some special characters (like newline and carriage return

characters).

To check if a file stream was successful opening a file, we can do it by calling to

member is_open(). This member function returns a bool value of true in the case that indeed

the stream object is associated with an open file, or false otherwise:

if (myfile.is_open()){ }

When we are finished with our input and output operations on a file we shall close it so that

the operating system is notified and its resources become available again. For that, we call the

stream's member function close. This member function takes flushes the associated buffers

and closes the file:

myfile.close();

Once this member function is called, the stream object can be re-used to open another file,

and the file is available again to be opened by other processes. In case an object is destroyed

while still associated with an open file, the destructor automatically calls the member

function close().

Page 7 of 24

Text files

Text file streams are those where the ios::binary flag is not included in their opening mode.

These files are designed to store text and thus all values that are input or output from/to them

can suffer some formatting transformations, which do not necessarily correspond to their

literal binary value.

State flags

The following member functions exist to check for specific states of a stream (all of them

return a bool value):

bad()

Returns true if a reading or writing operation fails. For example, in the case that we try to

write to a file that is not open for writing or if the device where we try to write has no space

left.

fail()

Returns true in the same cases as bad(), but also in the case that a format error happens, like

when an alphabetical character is extracted when we are trying to read an integer number.

eof()

Returns true if a file open for reading has reached the end.

good()

It is the most generic state flag: it returns false in the same cases in which calling any of the

previous functions would return true. Note that good and bad are not exact opposites

(good checks more state flags at once).

The member function clear() can be used to reset the state flags.

get and put stream positioning

All i/o streams objects keep internally at least one internal position:

ifstream, like istream, keeps an internal get position with the location of the element to be

read in the next input operation.

ofstream, like ostream, keeps an internal put position with the location where the next

element has to be written.

Finally, fstream, keeps both, the get and the put position, like iostream.

These internal stream positions point to the locations within the stream where the next

reading or writing operation is performed. These positions can be observed and modified

using the following member functions:

Page 8 of 24

tellg() and tellp()

These two member functions with no parameters return a value of the member

type streampos, which is a type representing the current get position (in the case of tellg) or

the put position (in the case of tellp).

seekg() and seekp()

These functions allow to change the location of the get and put positions. Both functions are

overloaded with two different prototypes. The first form is:

seekg ( position );

seekp ( position );

Using this prototype, the stream pointer is changed to the absolute position position (counting

from the beginning of the file). The type for this parameter is streampos, which is the same

type as returned by functions tellg and tellp.

The other form for these functions is:

seekg ( offset, direction );

seekp ( offset, direction );

Using this prototype, the get or put position is set to an offset value relative to some specific

point determined by the parameter direction. offset is of type streamoff. And direction is of

type seekdir, which is an enumerated type that determines the point from where offset is

counted from, and that can take any of the following values:

ios::beg offset counted from the beginning of the stream

ios::cur offset counted from the current position

ios::end offset counted from the end of the stream

Page 9 of 24

FUNCTIONS USED IN THE PROGRAM

1. void read_mat( )

This function opens the input file and reads the adjacency matrix. It checks whether there are

any errors in opening the file. The adjacency matrix is stored in the array city.

2. int BOR(int,int)

This function returns the binary OR result of the two input arguments. This is used in the

generation of transitive closure through Warshall’s algorithm.

3. void path_mat(int)

This function implements Warshall’s algorithm and forms the path matrix TC from the city

matrix. If the argument is 1, it additionally prints the generated transitive closure TC onto the

command line as well as the output file.

4. void push(int)

It pushes the argument supplied into the stack.

5. int pop( )

It returns the topmost element stored in the stack.

6. int compare(int)

It checks whether the argument already exists in the stack. If yes, it returns 1, else 0. This

function is used to determine whether a given node has already been traversed while

determining the paths between any two nodes of the graph.

7. void print_paths(int)

This method is responsible for the formatting and display of the output onto the command

line and output file. It is called from the find_all_paths( ) function as and when a path is

determined between the start_city and the end_city.

8. void find_all_paths( )

This function implements the algorithm to find all paths between any two given nodes. It

accepts two different nodes start_city and end_city. From both these numbers, 1 is

subtracted to make them in accordance to the array indices in the adjacency matrix. The

algorithm used is as follows:

The search starts from the row corresponding to the start_city. The algorithm

traverses all elements in this row.

If a 1 is encountered, it moves to the row corresponding to the column where the 1

was encountered. This city number is pushed into the stack.

If the whole row does not have any 1s, the function returns, printing “There is no

connection between these two cities”.

In this way, when the algorithm reaches the row corresponding to the end city, the

current path from the stack is printed. The topmost element is popped out. An

alternate path, if it exists, is searched from that index to the end_city.

When the end of the row of the start_city is encountered, the function returns.

Page 10 of 24

9. int closed_loop( )

This method determines if there exists a closed loop in the given graph. This is determined

from the path matrix. If there exists a 1 in the leading diagonal, that is, TC[i][i] =1, then there

is a loop originating and terminating at the city with index i. The function returns 1 if there

exists a loop, else 0.

10. void print( )

This function uses two nested for loops to print the adjacency matrix city which is read from

the input file.

11. int main( int argc , char* argv[ ] )

The main method is executed first in the program. This method takes in three command line

arguments, namely the program name, the input and output files. There is a provision to

check whether all the arguments have been supplied on program execution.

Two file objects are created, one each for the input and output files. The program is menu

driven, and exists completely within an infinite loop, which terminates only when the user

enters a 0. The user is provided the following options:

i. Print the path matrix.

ii. Find all paths between two cities.

iii. Check for existence of closed loops.

iv. Print the adjacency matrix.

v. Exit the program

Page 11 of 24

PROGRAM

#include<conio.h>

#include<fstream>

#include<iostream>

using namespace std;

const int NOC = 7; //Number of cities

int city[NOC][NOC], TC[NOC][NOC];

int top=-1, stack[NOC] , output_count = 1;

ofstream out;

ifstream in;

// TAKE INPUT FROM FILE

void read_mat()

{

//CHECK IF FILE IS OPENED

if ( in.good() == 0 )

{ cout<<"Sorry, an error occured!! Please restart the program.";

getch();

exit(0);

}

//END

// READ FROM FILE

for(int i=0; i<NOC ; i++ )

{ for ( int j=0 ; j <NOC ; j++ )

{

if ( !in.eof() )

{ in>>city[i][j];

}

}

}

//END

in.close();

}

/*------------------------------------------------------------------------*/

//FUNCTIONS ASSOCIATED WITH ALGORITHM 1

//BINARY OR OPERATION

int BOR( int A , int B)

{

if ( A == 0 && B == 0 )

return 0;

Page 12 of 24

else

return 1;

}

// WARSHALL'S ALGORITHM-TO GENERATE THE PATH MATRIX

void path_mat(int print)

{

for ( int i = 0 ; i<NOC ; i++ )

{ for ( int j =0 ; j<NOC ; j++ )

{ TC[i][j] = city[i][j];

}

}

//ALGORITHM STARTS HERE

for ( int j=0 ; j< NOC ; j++ )

{ for ( int i= 0 ; i< NOC ; i++ )

{

if ( TC[i][j] == 1 )

{

for ( int n = 0 ; n<NOC ; n++ )

{ TC[i][n] = BOR( TC[j][n] , TC[i][n] ) ;

}

}

}

}

//END

//PRINT PATH MATRIX

if ( print == 1)

{

out<<"\n\nOUTPUT "<<output_count<<" : \n\n";

output_count++;

cout<<"The path matrix is : \n\n";

out<<"The path matrix is : \n\n";

for ( int i=0 ; i<NOC ; i++ )

{ for ( int j=0 ; j<NOC ; j++ )

{ cout<<TC[i][j]<<" ";

out<<TC[i][j]<<" ";

}

cout<<"\n";

out<<"\n";

}

getch();

}

Page 13 of 24

//END

}

/*------------------------------------------------------------------------*/

// FUNCTIONS ASSOCIATED WITH ALGORITHM 2

void push( int element )

{

top++;

stack[top] = element ;

}

int pop()

{

int temp = stack[top];

top--;

return temp;

}

int compare(int element)

{

for ( int i=0 ; i<=top ; i++ )

{ if ( stack[i] == element )

return 1;

}

return 0;

}

void print_paths(int print)

{

if ( print == 1)

{ cout<<"\nPath number : ";

out<<"\nPath number : ";

}

cout<<"\n\n"<<print<<" : ";

out<<"\n\n"<<print<<" : ";

output_count++;

for ( int i=0 ; i<= top-1 ; i++ )

{ cout<<stack[i]+1<<" -> " ;

out<<stack[i]+1<<" -> " ;

}

cout<<stack[top]+1;

out<<stack[top]+1;

}

void find_all_paths()

{

int start_city, end_city , print_count = 1;

Page 14 of 24

out<<"\n\nOUTPUT "<<output_count<<" : \n\n"; output_count++;

cout<<"Enter cities between 1 - "<<NOC<<".\nEnter start city : ";

out<<"Enter cities between 1 - "<<NOC<<".\nEnter start city : ";

cin>>start_city;

out<<start_city<<"\n";

cout<<"Enter end city other than the starting city : ";

cin>>end_city;

out<<"Enter end city other than the starting city : ";

out<<end_city<<"\n";

cout<<"\n\n";

out<<"\n\n";

//TO MATCH USER INPUT TO MATRIX INDICES

end_city--;

start_city--;

// SEARCH ALGORITHM STARTS HERE

push(start_city);

int i = start_city , j= 0;

while ( true )

{

if(start_city==end_city){

cout<<"\nThe starting and the end cities cannot be the same!\n";

out<<"\nThe starting and the end cities cannot be the same!\n";

print_count=0;

break;

}

if((start_city+1)>NOC || (start_city+1)<=0 || (end_city+1)>NOC ||

(end_city+1)<=0){

cout<<"Please enter correct city numbers! Restart from main menu.";

out<<"Please enter correct city numbers! Restart from main menu.";

print_count=0;

break;

}

if ( i == end_city)

{ print_paths(print_count);

print_count++;

j = pop()+1;

if ( j > (NOC-1) ){

if(top>0) j = pop()+1;

else break;

}

i = stack[top];

}

Page 15 of 24

if ( j > (NOC-1) && i == start_city )

{

break;

}

if ( city[i][j] == 0 )

{

if( j == (NOC-1) )

{ if(i==start_city) break;

j = pop()+1;

if ( j > (NOC-1) ){

if(top>0) j = pop()+1;

else break;

}

i = stack[top];

}

else

j++;

}

else

{

if( compare(j) == 0 )

{ i=j;

push(j);

j=0;

}

else if( j == (NOC-1) )

{ j = pop()+1;

if ( j > (NOC-1) ){

if(top>0) j = pop()+1;

else break;

}

i = stack[top];

}

else

j++;

}

}

while( top != -1) //Simply to empty the stack

pop();

if ( print_count == 1 )

Page 16 of 24

{ cout<<"\nThere is no connection between these two cities.";

out<<"\nThere is no connection between these two cities.";

}

else if(print_count>1)

{ cout<<"\n\nThese are all the available paths!";

out<<"\nThese are all the available paths!";

}

getch();

}

/*------------------------------------------------------------------------*/

//ALGORITHM 3

int closed_loop()

{

path_mat(0);

for(int i = 0; i<NOC ; i++ )

{ if ( TC[i][i] == 1)

return 1;

}

return 0;

}

/*------------------------------------------------------------------------*/

//PRINT ADJACENCY MATRIX

void print()

{

out<<"\n\nOUTPUT "<<output_count<<" : \n\n";

output_count++;

cout<<"The adjacency matrix is : \n\n";

out<<"The adjacency matrix is : \n\n";

for ( int i=0 ; i<NOC ; i++ )

{ for ( int j=0 ; j<NOC ; j++ )

{ cout<<city[i][j]<<" ";

out<<city[i][j]<<" ";

}

cout<<"\n";

out<<"\n";

}

getch();

Page 17 of 24

}

/*------------------------------------------------------------------------*/

int main(int argc, char* argv[])

{

if(argc!=3) {

cout<<"Enter the program with the input and output file names.";

return 1;

}

out.open(argv[2]);

in.open(argv[1]);

read_mat();

while(1)

{

cout<<endl<<"\n1. Print path matrix - transitive closure for the given city matrix.";

cout<<"\n2. Find all paths between two cities.";

cout<<"\n3. Check for existence of loops.";

cout<<"\n4. Print the adjacency matrix.";

cout<<"\n0. Exit";

cout<<"\n\nEnter choice : ";

out<<"\n\nOUTPUT "<<output_count<<" : "; output_count++;

out<<"\n\n1. Print path matrix - transitive closure for the given city matrix.";

out<<"\n2. Find all paths between two cities.";

out<<"\n3. Check for existence of loops.";

out<<"\n4. Print the adjacency matrix.";

out<<"\n0. Exit";

out<<"\n\nEnter choice : ";

int choice;

cin>>choice;

out<<choice<<"\n";

switch(choice)

{

case 1 : { path_mat(1);

break;

}

case 2 : { find_all_paths();

break;

}

Page 18 of 24

case 3 : {

int tru = closed_loop();

if ( tru == 1 ){

cout<<"\n\nThe given city graph has closed loops!";

out<<"\n\nThe given city graph has closed loops!";

}

else{

cout<<"\n\nThe given city graph does not have closed loops!";

out<<"\n\nThe given city graph does not have closed loops!";

}

getch();

break;

}

case 4 : { print();

break;

}

case 0 :return 0;

}

if( choice != 1 || choice!= 2 || choice != 3 || choice != 4 || choice != 0 )

{ cout<<"\nWrong choice. Please enter again!";

getch();

out<<"\nWrong choice. Please enter again!";

}

}

out.close();

}

Page 19 of 24

OUTPUT

Input file contents (adjacency matrix):

0 0 0 0 0 0 0

0 0 1 0 1 0 1

0 0 0 1 0 0 0

0 0 1 0 1 1 0

0 0 0 0 0 1 0

0 0 1 0 0 0 1

1 0 1 0 0 1 0

Command line output:

C:\Users\Raghav\Desktop\DS Project> proj adja.txt output.txt

1. Print path matrix - transitive closure for the given city matrix.

2. Find all paths between two cities.

3. Check for existence of loops.

4. Print the adjacency matrix.

0. Exit

Enter choice : 4

The adjacency matrix is :

0 0 0 0 0 0 0

0 0 1 0 1 0 1

0 0 0 1 0 0 0

0 0 1 0 1 1 0

0 0 0 0 0 1 0

0 0 1 0 0 0 1

1 0 1 0 0 1 0

1. Print path matrix - transitive closure for the given city matrix.

2. Find all paths between two cities.

3. Check for existence of loops.

4. Print the adjacency matrix.

0. Exit

Enter choice : 1

The path matrix is :

Page 20 of 24

0 0 0 0 0 0 0

1 0 1 1 1 1 1

1 0 1 1 1 1 1

1 0 1 1 1 1 1

1 0 1 1 1 1 1

1 0 1 1 1 1 1

1 0 1 1 1 1 1

1. Print path matrix - transitive closure for the given city matrix.

2. Find all paths between two cities.

3. Check for existence of loops.

4. Print the adjacency matrix.

0. Exit

Enter choice : 2

Enter cities between 1 - 7.

Enter start city : 3

Enter end city other than the starting city : 5

Path number :

1 : 3 -> 4 -> 5

These are all the available paths!

1. Print path matrix - transitive closure for the given city matrix.

2. Find all paths between two cities.

3. Check for existence of loops.

4. Print the adjacency matrix.

0. Exit

Enter choice : 2

Enter cities between 1 - 7.

Enter start city : 1

Enter end city other than the starting city : 5

There is no connection between these two cities.

1. Print path matrix - transitive closure for the given city matrix.

2. Find all paths between two cities.

3. Check for existence of loops.

4. Print the adjacency matrix.

0. Exit

Page 21 of 24

Enter choice : 2

Enter cities between 1 - 7.

Enter start city : 3

Enter end city other than the starting city : 6

Path number :

1 : 3 -> 4 -> 5 -> 6

2 : 3 -> 4 -> 6

These are all the available paths!

1. Print path matrix - transitive closure for the given city matrix.

2. Find all paths between two cities.

3. Check for existence of loops.

4. Print the adjacency matrix.

0. Exit

Enter choice : 3

The given city graph has closed loops!

1. Print path matrix - transitive closure for the given city matrix.

2. Find all paths between two cities.

3. Check for existence of loops.

4. Print the adjacency matrix.

0. Exit

Enter choice : 0

C:\Users\Raghav\Desktop\DS Project>

Output file contents:

OUTPUT 1 :

1. Print path matrix - transitive closure for the given city matrix.

2. Find all paths between two cities.

3. Check for existence of loops.

4. Print the adjacency matrix.

0. Exit

Page 22 of 24

Enter choice : 4

OUTPUT 2 :

The adjacency matrix is :

0 0 0 0 0 0 0

0 0 1 0 1 0 1

0 0 0 1 0 0 0

0 0 1 0 1 1 0

0 0 0 0 0 1 0

0 0 1 0 0 0 1

1 0 1 0 0 1 0

OUTPUT 3 :

1. Print path matrix - transitive closure for the given city matrix.

2. Find all paths between two cities.

3. Check for existence of loops.

4. Print the adjacency matrix.

0. Exit

Enter choice : 1

OUTPUT 4 :

The path matrix is :

0 0 0 0 0 0 0

1 0 1 1 1 1 1

1 0 1 1 1 1 1

1 0 1 1 1 1 1

1 0 1 1 1 1 1

1 0 1 1 1 1 1

1 0 1 1 1 1 1

OUTPUT 5 :

1. Print path matrix - transitive closure for the given city matrix.

2. Find all paths between two cities.

3. Check for existence of loops.

4. Print the adjacency matrix.

0. Exit

Enter choice : 2

Page 23 of 24

OUTPUT 6 :

Enter cities between 1 - 7.

Enter start city : 3

Enter end city other than the starting city : 5

Path number :

1 : 3 -> 4 -> 5

These are all the available paths!

OUTPUT 8 :

1. Print path matrix - transitive closure for the given city matrix.

2. Find all paths between two cities.

3. Check for existence of loops.

4. Print the adjacency matrix.

0. Exit

Enter choice : 2

OUTPUT 9 :

Enter cities between 1 - 7.

Enter start city : 1

Enter end city other than the starting city : 5

There is no connection between these two cities.

OUTPUT 10 :

1. Print path matrix - transitive closure for the given city matrix.

2. Find all paths between two cities.

3. Check for existence of loops.

4. Print the adjacency matrix.

0. Exit

Enter choice : 2

OUTPUT 11 :

Enter cities between 1 - 7.

Enter start city : 3

Enter end city other than the starting city : 6

Page 24 of 24

Path number :

1 : 3 -> 4 -> 5 -> 6

2 : 3 -> 4 -> 6

These are all the available paths!

OUTPUT 14 :

1. Print path matrix - transitive closure for the given city matrix.

2. Find all paths between two cities.

3. Check for existence of loops.

4. Print the adjacency matrix.

0. Exit

Enter choice : 3

The given city graph has closed loops!

OUTPUT 15 :

1. Print path matrix - transitive closure for the given city matrix.

2. Find all paths between two cities.

3. Check for existence of loops.

4. Print the adjacency matrix.

0. Exit

Enter choice : 0