Você está na página 1de 38

5th semester ADA lab 1

5th SEMESTER ISE MANUAL FOR ANALYSIS AND DESIGN OF


ALGORITHMS (ADA) LAB

LIST OF PROGRAMS

1. Implement Recursive Binary search and Linear search and determine the time required to
search an element. Repeat the experiment for different values of n , the number of
elements in the list to be searched and plot a graph of the time taken versus n .
2. Sort a given set of elements using the Heapsort method and determine the time required
to sort the elements. Repeat the experiment for different values of n , the number of
elements in the list to be searched and plot a graph of the time taken versus n .
3. Sort a given set of elements using Merge sort method and determine the time required to
sort the elements. Repeat the experiment for different values of n , the number of
elements in the list to be searched and plot a graph of the time taken versus n .
4. Sort a given set of elements using Selection sort and determine the time required to sort
the elements. Repeat the experiment for different values of n , the number of elements in
the list to be searched and plot a graph of the time taken versus n .
5. a. Obtain the topological ordering of vertices in a given digraph.
b. Implement All Pair Shortest paths problem using Floyd’s algorithm.
6. Implement the 0/1 knapsack problem using dynamic programming.
7. From a given vertex in a weighted connected graph, find shortest paths to other vertices
using Dijkstra’s algorithm.
8. Sort a given set of elements using Quick sort method and determine the time required to
sort the elements. Repeat the experiment for different values of n , the number of
elements in the list to be searched and plot a graph of the time taken versus n .
9. Find minimum cost spanning tree of a given undirected graph using Kruskal’s algorithm.
10. a. Print all the nodes reachable from a given starting node in a digraph using BFS
method.
b. Check whether a graph is connected or not using DFS method.
11. Find a subset of a given set S = {s1 , s2 ,L, sn } of n positive numbers whose sum is equal to
a given positive number d . For example, if S = {1,2,5,6,8} and d = 9 , there are 2
solutions {1,2,6} and {1,8} . A suitable message is to be displayed if the given problem
instance does not have a solution.
12. a. Implement Horspool algorithm for String Matching.
b. Find the Binomial Co-efficients using Dynamic Programming.
13. Find the Minimum Cost Spanning Tree of a given undirected graph using Prim’s
algorithm.
14. a. Implement Floyd’s algorithm for the All-Pairs-Shortest-Paths problem.
b. Compute the transitive closure of a given directed graph using Warshall’s algorithm.
15. Implement n-queens problem using Back tracking.

NOTE: In the examination, questions must be given based on lots.

BNMIT ISE DEPT.


5th semester ADA lab 2

TUTORIAL: How to determine running-time of code and to plot a graph or running-time versus
number of elements.

In programs 1, 2, 3, 4 and 8, it is required to determine running time of the code and plot
it on a graph for different values of n . Below is some code and explanation which shows how to
do this. This code portion must then be incorporated into each of the programs 1,2,3,4 and 8 by
using a switch statement. In this, one case is for the regular running of the function for small
sizes of inputs and for testing whether the programs actually work. The other case is for running
the program with large number of inputs, measuring running time and writing running time data
into a file for plotting as a graph later. The following example of Bubble sort demonstrates this.

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <values.h>
#include <alloc.h>
#include <dos.h>

void bubble(int far* a, int n)


{ int i,j,temp;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{ if (a[i] > a[j])
{ temp = a[i];
a[i]=a[j];
a[j]=temp;
}
}
}

void main()
{ FILE* fp;
5 clock_t c1,c2;
int i,n=1000,data_size=0;
int far* a;
fp=fopen("values.dat","w+");
if (fp == NULL)
{ printf("cannot open file\n");
exit(0);
}
while(data_size <=10)
{ a=(int far*)farcalloc(n,sizeof(int));
if (a==NULL)
{ printf("insufficient memory\n");
exit(0);
}
randomize();
for(i=1;i<=n;i++) a[i]=rand() % MAXINT;
c1=clock();
bubble(a,n); /*replace this by the function to be evaluated*/
c2=clock();
farfree(a);
if ((c2-c1) != 0)
{ fprintf(fp,"%d\t%f\n",n,(c2-c1)/CLK_TCK);

BNMIT ISE DEPT.


5th semester ADA lab 3

data_size++;
}
n=n+1000;
}
fclose(fp);
}

In the main function, a file called values.dat (which, by default is created in c:\tc\bin
folder) is opened into which the code writes 2 values per line and these values are separated by a
single space or more (a tab). The first value is n and the second value is the time in seconds
taken by the processor to execute the function whose running time is to be determined. The value
of n starts from 1000 because if n is below that, then the execution time generally clocks zero
or very close to zero and this is clumsy to graph. n is incremented by 1000 each time and, totally
10 non-zero running time values are collected. The file, whose name is specified, is opened in w+
mode, which means that if a file of the same name is already there, then that file is opened for
reading and writing and the earlier contents are overwritten. Each line is written into the file
using the fprintf function. This file is closed at the end of the main function using fclose
function. Functions fopen, fclose and fprintf and the type FILE are defined in the header file
stdio.h.
Since the main function is dealing with large arrays, it is efficient to dynamically allocate
these large arrays. Because of this, the memory model of the C compiler must be changed to
`Huge’ to accommodate these large arrays (In the TurboC IDE, go to Options → Compiler →
Code generation → Model, and make it `Huge’). The function farcalloc is used to allocate
these arrays because their sizes might exceed 64kB in size, and it returns a pointer of type int
far* to access the allocated memory. Correspondingly, the function farfree is used to
deallocate the allocated memory. These functions are defined in the header file alloc.h.
To count the number of seconds elapsed, the number of clock ticks elapsed during the
execution of the function whose running time is to be determined is done as follows: 2 variables
c1 and c2 of type clock_t are declared, and the function clock is executed just before and just
after the calling the function under evaluation. The clock function returns a value of type
clock_t, so by subtracting c1 from c2, the number of clock ticks elapsed is obtained. To get the
seconds elapsed, the number of clock ticks is divided by the constant CLK_TCK, which gives the
number of clock ticks per second. This constant, the function clock and the type clock_t are
defined in the header file time.h.
The dynamically allocated array is populated with numbers obtained randomly using the
functions randomize and rand. The function rand actually generates the random numbers, and
the function randomize re-initializes the seed of the random number generator each time it is
executed. These functions are defined in the header file stdlib.h.
The file values.dat looks typically as follows:

2000 0.109890
3000 0.219780
4000 0.439560
5000 0.604396
6000 0.879121
7000 1.153846
8000 1.538462
9000 1.923077
10000 2.417582

BNMIT ISE DEPT.


5th semester ADA lab 4

11000 2.912088
12000 3.406593

A graphing program called gnuplot is used to draw a graph on the input set. It is
installed in c:\gnuplot and its executable is named as wgnuplot.exe. It is executed by opening
a DOS command window, going to the folder where gnuplot is installed, typing wgnuplot.exe
and pressing the enter key.

The following window on the left appears as soon as wgnuplot.exe is executed.

gnuplot takes its input from a file in which each x and y coordinate are specified in each
line separated by a space or tab. But first, the command called plot needs to be used to tell
gnuplot that we need it to plot a graph. This is done by clicking on the menu item Plot as seen
above on the right. When this is done, the word plot comes on the command line of gnuplot.
Then, the menu item Data Filename… under Plot must be clicked and gnuplot must be given
the file which was created from the above C program. On doing this, the text plot
c:\tc\bin\values.dat comes on the command line of gnuplot. Lines are to be drawn through
the data points, so plot c:\tc\bin\values.dat with lines is to be typed and enter key must
be pressed. The graph, as seen below, comes up.

BNMIT ISE DEPT.


5th semester ADA lab 5

If the X-axis and Y-axis labels need to be changed as seen in the graph below, then the
commands set xlabel “input size n” and set ylabel “running time in seconds”
need to be typed before executing the plot command.

If the running times of two functions need to be compared on the same graph, assuming
that their running time data files are available at values1.dat and values2.dat, one needs to
type plot values1.dat with lines, values2.dat with lines.

BNMIT ISE DEPT.


5th semester ADA lab 6

PROGRAM 1: Implement Recursive Binary search and Linear search and determine the time
required to search an element. Repeat the experiment for different values of n , the number of
elements in the list to be searched and plot a graph of the time taken versus n .

#include <stdio.h>
#include <conio.h>

int linsrch(int a[],int key,int n)


{ if (n == 0) return 0;
if (a[n] == key) return n;
else return linsrch(a,key,n-1);
}

int binsrch(int a[],int key,int low, int high)


{ int mid;
if (low>high) return 0;
else
{ mid=(low+high)/2;
if (a[mid] == key) return mid;
else if (a[mid] < key) return binsrch(a,key,mid+1,high);
else return binsrch(a,key,low,mid-1);
}
}

void main()
{ int n,ch,i,a[10],key,flag;
char ok='y';
clrscr();
while (ok !='n' || ok !='N')
{ printf("enter number of numbers to search in\n");
scanf("%d",&n);
printf("enter key to search\n");
scanf("%d",&key);
printf("1:Recursive linear search\n2:Recursive binary search\n");
printf("any other key to exit\n");
printf("enter your choice\n");
scanf("%d",&ch);
switch(ch)
{ case 1: printf("enter numbers in any order\n");
for(i=1;i<=n;i++) scanf("%d",&a[i]);
flag=0;
flag=linsrch(a,key,n);
if (flag != 0) printf("search key found at position
%d\n",flag);
else printf("search key not found\n");
break;
case 2: printf("enter numbers in ascending order\n");
for(i=1;i<=n;i++) scanf("%d",&a[i]);
flag=0;
flag=binsrch(a,key,1,n);
if (flag != 0) printf("search key found at position
%d\n",flag);
else printf("search key not found\n");
break;
default : break;
}

BNMIT ISE DEPT.


5th semester ADA lab 7

printf("press y or Y to continue, n or N to quit\n");


fflush(stdin);
ok=getc(stdin);
if (ok =='y' || ok == 'Y') continue;
else exit();
}
}

The function linsrch, which does linear search, is an example for Brute force technique.
The recursion checks each item with the key. It is convenient to search from n down to 1 instead
of going from 1 to n, since n is being passed to the function in the call from the main function.
Recursion stops when n reaches 0 as we have entered data from a[1] and not from a[0]. This
means the function did not find the key and the search is a failure. The running time is O (n)
since all n elements are compared with the key.
The function binsrch, which does binary search on a list of elements sorted in
ascending order, is an example for Divide and conquer strategy. The list of elements is split into
two halves each time by computing a mid value. This is the divide step. The key being searched
for is compared with a[mid], and if the values are equal, the search is a success. This is the
conquer step. If not, then if the key is smaller than a[mid], it is searched for in the lower half of
the list, and the upper half of the list is discarded. If the key is larger than a[mid], it is searched
for in the upper half of the list, and the lower half of the list is discarded. This goes on till the
subproblem size reduces to 1 and recursion stops here (because low = high at this point, and
low, high and mid all point to the same element) after checking that single element whether it is
equal to the search key. If not, then the search is a failure.
Based on this, the recurrence relation for binary search can be written as
T (n) = 1.T (n / 2) + O (1) . The divisor 2 in T (n / 2) signifies that the problem is halved each time.
The multiplier 1 beside T (n / 2) signifies that of the two subproblems created, only one of them
needs to be solved (upper half or lower half). The O (1) in the equation signifies that to divide the
problem and to check whether the element a[mid] is equal to the search key, it takes O (1) time.
There is no need for any merge step here. On solving this recurrence using Master theorem or
backward substitution, we get the running time of binary search as O (log n) .

BNMIT ISE DEPT.


5th semester ADA lab 8

PROGRAM 2: Sort a given set of elements using the Heapsort method and determine the time
required to sort the elements. Repeat the experiment for different values of n , the number of
elements in the list to be searched and plot a graph of the time taken versus n .

#include <stdio.h>
#include <conio.h>

void heapify(int a[], int n)


{ int i,j,k,item;
for(k=2;k<=n;k++)
{ item = a[k];
i=k, j=i/2;
while ((i>1) && (item > a[j]))
{ a[i] = a[j];
i=j, j=i/2;
}
a[i]=item;
}
}

void adjust(int a[], int n)


{ int i,j,item;
j=1, item=a[j], i=2*j;
while (i <= n)
{ if ((i+1 <= n) && (a[i] < a[i+1])) ++i;
if (item < a[i])
{ a[j] = a[i];
j=i, i=2*j;
}
else break;
}
a[j]=item;
}

void heapsort(int a[],int n)


{ int i,temp;
heapify(a,n);
for(i=n;i>1;i--)
{ temp = a[i];
a[i] = a[1];
a[1] = temp;
adjust(a,i-1);
}
}

void main()
{ int n,i,a[10];
clrscr();
printf("enter number of numbers to be sorted\n");
scanf("%d",&n);
printf("enter numbers to be sorted\n");
for(i=1;i<=n;i++) scanf("%d",&a[i]);
heapsort(a,n);
printf("the sorted list is\n");
for(i=1;i<=n;i++) printf("%d\n",a[i]);
getch();

BNMIT ISE DEPT.


5th semester ADA lab 9

This problem is an example for Transform and Conquer strategy. To sort a set of
numbers, the set is transformed into a heap, and then conquered.
This code sorts numbers in ascending order. For this reason, a max-heap is implemented
here. A max-heap has the following properties: a) the parent is larger than both its children, b)
the elements are filled into a heap from left to right, and, c) if a parent’s number is i, its left child
can be accessed by 2i and its right child by 2i+1. (Only property a) changes for a min-heap.)
In each step of heap sort, a max-heap is created, because of which the largest element
comes to the top of the heap. Then, this element is exchanged with the last element of the heap
(this last element need not be the smallest element of the list). The heap is implemented in an
array, so the largest element comes to its final resting position (in the sorted list) and is not
considered anymore for sorting. This functionality is implemented in the function heapsort.
The function heapsort, which is called from the main function, has two functions,
heapify and adjust. The function heapify takes an array of numbers and makes a max-heap
out of the list by using the top-down heap creation technique. It starts from the second element
(left child of the root), compares it with its parent and, if found to be greater than its parent,
moves it to the top. In general, for some child a[k], its parent being a[k/2], if a[k/2] is smaller
than a[k], then they are exchanged. This exchange may propagate all the way up to the top of
the heap. This function has a running time of O (n log n) and is performed only once, just before
the sorting process starts.
Once a heap is created, the largest element comes to the top of the heap, which is then
exchanged with the last element. Because of this exchange, the data structure is no longer a max-
heap. Only one path from the root level to the leaf level needs to be examined and adjusted to
restore max-heap property. To accomplish this, the function adjust examines a parent and its
two children each time from top to bottom and exchanges the elements as required. This function
takes O (log n) time as the height of the heap (which is also a balanced binary tree) is O (log n) .
A note: The program will work correctly even if, in the function heapsort, function call
adjust(a,i-1) is replaced with heapify(a,i-1). That is, the function adjust is not required
at all. But then this implies that, each time a root and last elements are exchanged and max-heap
property is to be restored, we have to call function heapify. Since there are n elements in total,
calling heapify n times will make Heap sort run in O (n 2 log n) time. But by using function
adjust to restore heap property, we need to call it n times, so running time becomes O ( n log n) .

BNMIT ISE DEPT.


5th semester ADA lab 10

PROGRAM 3: Sort a given set of elements using Merge sort method and determine the time
required to sort the elements. Repeat the experiment for different values of n , the number of
elements in the list to be searched and plot a graph of the time taken versus n .

#include <stdio.h>
#include <conio.h>

void simplemerge(int a[],int low,int mid,int high)


{ int b[10],i=low,h=low,j=mid+1,k;
while ((h<=mid) && (j<=high))
{ if (a[h] <= a[j]) b[i++]=a[h++];
else b[i++]=a[j++];
}
if (h > mid)
for(k=j;k<=high;k++) b[i++]=a[k];
else
for(k=h;k<=mid;k++) b[i++]=a[k];
for(k=low;k<=high;k++) a[k]=b[k];
}

void mergesort(int a[],int low,int high)


{ int mid;
if (low<high)
{ mid=(low+high)/2;
mergesort(a,low,mid);
mergesort(a,mid+1,high);
simplemerge(a,low,mid,high);
}
}

void main()
{ int n,i,a[10];
clrscr();
printf("enter number of numbers to sort\n");
scanf("%d",&n);
printf("enter the numbers to sort\n");
for (i=1;i<=n;i++) scanf("%d",&a[i]);
mergesort(a,1,n);
printf("the sorted list of numbers are\n");
for (i=1;i<=n;i++) printf("%d\n",a[i]);
getch();
}

This is a problem in Divide and conquer. Like in Binary search, the list of numbers is
halved every time, but unlike binary search, no part of the list can be discarded, since we have to
sort all the numbers.
The function mergesort divides the list of numbers recursively into 2 parts, one from
low to mid and another from mid+1 to high. This is the divide step. The merging step happens in
the function simplemerge. It merges two sorted lists, one from low to mid and another from
mid+1 to high, and this is the conquer step. It first compares the first elements in both lists. If the
first element of the first list is smaller than the first element of the second list, then the first few
elements of the first list is copied into a third list till a number is found in the first list which is
larger than the first element of the second list. Vice-versa if the first element of the second list is

BNMIT ISE DEPT.


5th semester ADA lab 11

smaller than the first element of the first list. As each list is O (n) in size, the running time of
simplemerge is O (n) .
Alternating copying of elements from both lists may end up in one of the lists getting
completely copied into the third list earlier than the other list. This happens when the list are not
equal in size (but their size will both be O (n) ). When this happens, the contents of the remaining
list are simply copied into the third list. The rest of the program does not know of the existence
of the third list, so at the end of simplemerge, this third list contents are overwritten onto the
original list itself.
Based on this, the recurrence relation for merge sort can be written as
T (n) = 2T (n / 2) + O (n) . The divisor 2 in T (n / 2) signifies that the problem is halved each time.
The multiplier 2 beside T (n / 2) signifies that both the subproblems created by the divide step are
to be solved. The O (n) signifies that to divide a problem into subproblems, to solve individual
subproblems and to merge their individual solutions, it takes O (n) time. On solving this
recurrence relation using Master theorem or backward substitution, we get O (n log n) as the
running time of the whole algorithm.

BNMIT ISE DEPT.


5th semester ADA lab 12

PROGRAM 4: Sort a given set of elements using Selection sort and determine the time required
to sort the elements. Repeat the experiment for different values of n , the number of elements in
the list to be searched and plot a graph of the time taken versus n .

#include <stdio.h>
#include <conio.h>

void selsort(int a[],int n)


{ int i,j,small,temp;
for(i=1;i<=n-1;i++)
{ small=i;
for(j=i+1;j<=n;j++)
if (a[j] <= a[i]) small=j;
temp=a[i];
a[i]=a[small];
a[small]=temp;
}
}

void main()
{ int i,n,a[10];
clrscr();
printf("enter number of numbers to sort\n");
scanf("%d",&n);
printf("enter numbers to sort\n");
for(i=1;i<=n;i++) scanf("%d",&a[i]);
selsort(a,n);
printf("sorted list is\n");
for(i=1;i<=n;i++) printf("%d\n",a[i]);
getch();
}

This problem falls under the Brute force strategy. The idea is to scan the given array to
find out the smallest element, and once found, it is exchanged with the first element. Then, the
remainder of the array is scanned to find the next smallest element, and once found it is
exchanged with the second element. This is done till the (n − 1)th element.
To find the smallest element for the first time, it takes n − 1 comparisons. To find the
second smallest element, it takes n − 2 comparisons. To find the (n − 1)th smallest element, it
takes 1 comparison. On adding all these numbers of comparisons, we get
1 + 2 + 3 + L + + (n − 2) + (n − 1) which solves to O (n 2 ) .

BNMIT ISE DEPT.


5th semester ADA lab 13

PROGRAM 5a: Obtain the topological ordering of vertices in a given digraph.

#include <stdio.h>
#include <conio.h>

struct stack
{ int content[20],top;
}s;

void push (int i)


{ s.content[++(s.top)] = i;
}

int pop ()
{ return s.content[(s.top)--];
}

int is_empty ()
{ if (s.top == -1) return 1;
return 0;
}

void topo_order(int adjm[][10], int to[], int n)


{ int i,j,k=1,in[10],deg;
s.top=-1;
for(i=1;i<=n;i++)
{ deg=0;
for(j=1;j<=n;j++) if (adjm[j][i] == 1) deg++;
in[i]=deg;
}
for(;;)
{ for(i=1;i<=n;i++)
if (in[i] == 0)
{ push(i);
in[i]--;
}
if (is_empty()) break;
to[k]=pop();
for(i=1;i<=n;i++) if (adjm[to[k]][i] == 1) in[i]--;
k++;
}
if (k<n)
{ printf("topological sorting not possible\n");
exit();
}
}

void main()
{ int n,adjm[10][10],i,j,to[10];
clrscr();
printf("enter the number of vertices in the DAG for topological
sort\n");
scanf("%d",&n);
printf("enter the adjacency matrix of the DAG\n");
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)

BNMIT ISE DEPT.


5th semester ADA lab 14

scanf("%d",&adjm[i][j]);
topo_order(adjm,to,n);
printf("the topological ordering of the vertices in entered DAG is\n");
for(i=1;i<=n;i++) printf("%d ",to[i]);
getch();
}

Topological sort is done on directed graphs and can be done in two different ways. One is
by vertex removal. In this method, a vertex which has no in-edges (i.e., it has only out-edges) is
found and is deleted along with all its out-edges. The order in which these vertices are deleted is
kept track of, and when there are no more vertices to delete, the previously mentioned order is
reversed and displayed.
The other method is by DFS. This method starts off by doing DFS on the given digraph.
The order in which these vertices become dead ends is kept track of, and then when there is
nowhere else for the DFS to visit, the previously mentioned order is reversed and displayed.
It is obvious from the above description that, no matter which method is used, it is
convenient to implement a stack, so that reversing the order in which vertices were inserted into
the stack is easy. The first method is implemented in the code here. A structure called stack is
created, which has a pointer called top to the topmost element of the stack.
The number of in-edges to a vertex is counted and stored in an array called in. So, in[i]
gives the number of in-edges to vertex i. The cycle starts by searching for some vertices in the
given digraph which has no in-edges, i.e., in[i] is 0 for such vertices (In the first cycle of the
infinite for loop, this happens even without having to delete any vertex). If there are, they are
searched for in the array in and pushed onto the stack. Once all such vertices are found, they are
popped from the stack and inserted into an array, so that we get the reverse order. Then the
entries of such vertices in the array in are made − 1 so that they are not counted again in the
next iteration, hence effectively deleting them. Having done so, the edges going out of them also
cease to exist, so that the in-degree of those vertices to which these out-going edges point to are
reduced by 1.
The program ends when there are no more items on the stack. Integer k keeps track of
how many vertices were pushed/popped onto/from the stack. If k < n, then all the vertices in the
graph were not pushed/popped, which means that the in array’s entry for such vertices never
became 0. This implies that there is a cycle in the entered digraph. But, as topological sort is
defined for acyclic graphs, the program prints that there is no solution.
Reading the adjacency matrix takes O (n 2 ) time. To find one vertex with zero in-edges,
we scan the in array entries of all n vertices, so this takes O (n) , hence O (n 2 ) for all vertices.
On the whole, the running time of this implementation is O (n 2 ) .

BNMIT ISE DEPT.


5th semester ADA lab 15

PROGRAM 5b: Implement All Pair Shortest paths problem using Floyd’s algorithm.

#include <stdio.h>
#include <conio.h>

int min(int a,int b)


{ return (a<b)?a:b;
}

void floyd(int cost[][10], int n)


{ int i,j,k;
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
cost[i][j] = min(cost[i][j],
cost[i][k]+cost[k][j]);
}

void main()
{ int n, i,j,cost[10][10];
clrscr();
printf("enter the number of vertices\n");
scanf("%d",&n);
printf("enter the cost matrix of graph, with 999 for infinity\n");
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
scanf("%d", &cost[i][j]);
floyd(cost,n);
printf("the shortest distance matrix from all to all vertices are\n");
for(i=1;i<=n;i++)
{ for(j=1;j<=n;j++)
printf("%d ",cost[i][j]);
printf("\n");
}
getch();
}

In graphs, if the shortest paths from vertex a to b and b to c are known, then the shortest
path from a to c is known. So, getting the shortest path from a to c requires solving two
subproblems. Now, if the shortest path from a to d is required, and c is connected to d by an
edge, then it is required to solve the shortest path queries from a to b, b to c and c to d. Here,
finding shortest paths from a to b and from b to c are common subproblems to both above
problems, and need not be solved again and again, but can be stored in a table. This is why
Floyd’s algorithm is an example for the dynamic programming technique.
To actually compute the shortest paths between two vertices, Floyd’s algorithm considers
a pair of vertices u and v at a time and all other vertices as intermediate vertices through which
the shortest path between u and v might be passing. This check takes O (n) time per pair of
vertices. There are O (n 2 ) pair of vertices, so the overall running time for this algorithm is
O (n 3 ) .

BNMIT ISE DEPT.


5th semester ADA lab 16

PROGRAM 6: Implement the 0/1 knapsack problem using dynamic programming.

#include <stdio.h>
#include <conio.h>

int max(int a,int b)


{ return (a>b)?a:b;
}

int MFknapsack(int i, int j, int p[], int w[], int v[][11])


{ int value;
if (v[i][j] < 0)
{ if (j< w[i]) value = MFknapsack(i-1,j,p,w,v);
else
value = max(MFknapsack(i-1,j,p,w,v),
p[i]+MFknapsack(i-1,j-w[i],p,w,v));
v[i][j]=value;
}
return v[i][j];
}

void main()
{ int n,i,j,p[10],w[10],v[11][11],val,C;
clrscr();
printf("enter number of objects\n");
scanf("%d",&n);
printf("enter the capacity of the knapsack\n");
scanf("%d",&C);
printf("enter the profit of each of %d objects\n",n);
for(i=1;i<=n;i++) scanf("%d",&p[i]);
printf("enter the weight of each of %d objects\n",n);
for(i=1;i<=n;i++) scanf("%d",&w[i]);
for(i=0;i<=n;i++)
for(j=0;j<=C;j++)
if ((i==0) || (j==0)) v[i][j]=0;
else v[i][j]=-1;
val=MFknapsack(n,C,p,w,v);
printf("the total value of the objects in the knapsack is: %d\n",val);
printf("the objects picked up into the sack are: ");
i=n, j=C;
while (i>0)
{ if (v[i][j] != v[i-1][j])
{ printf("%d ",i);
j=j-w[i], i--;
}
else i--;
}
getch();
}

A knapsack of capacity C and n items, each with profit p[i] and weight w[i], are given.
Some subset of the n items is to be placed into the knapsack so that the total value of items in
the knapsack is maximized while not exceeding the capacity of the knapsack.
This problem can be solved in 3 ways: top-down method, bottom-up dynamic
programming method and memory function dynamic programming method. The top-down

BNMIT ISE DEPT.


5th semester ADA lab 17

approach is done as follows: to solve the problem of placing a i -sized subset of n items in the
knapsack while maximizing the value, one has to solve the problem of placing a i − 1 sized
subset of n items in the knapsack while maximizing the value. The i − 1th item may be placed or
not. The subproblems need to be kept track of, and there might be repeated/overlapping
subproblems to solve. This takes exponential time, and the running-time analysis is similar to
that of generating Fibonacci numbers using top-down approach.
A good idea is to use a table to keep track of the subproblems. A 2D array v is used to do
this, i.e., what is the value of the items in the knapsack if the i th item is put into it, or what is the
value if the i th item is not put into it. If there are repeated subproblems, they are solved only
once and stored into the corresponding entry in array v, and this value is simply used whenever
the same subproblem arises again. The table size will simply be nC and all values in the table
have to be computed. Column and row 0 are initialized to 0. This is because v[k][0] = 0 means
that the kth item is the first one to be considered and the knapsack is empty, so there is no value.
Conversely, v[0][k] = 0 means that if no item is considered but the knapsack is considered to
be k full, still the knapsack is empty and there is no value. Among O (nC ) entries, many entries
correspond to infeasible placements, so computing all nC entries is overkill.
Yet another idea is to combine both top-down approach and bottom-up dynamic
programming approach. The solution starts by considering the problem of placing n objects in
the C -sized knapsack, and corresponding value in the table v[n][C] is made − 1 to represent
this fact. Two subproblems are generated from this, i.e., the problem of placing the n − 1th object
or the problem of not placing the n − 1th object. Because of this, all the elements in the n − 1th
row of the table v are not computed. So, all nC values are not computed using this approach,
which makes it the most efficient approach among the three approaches.
All values of the 2D array v are initialized to -1, except values in the 0th column and
values in the 0th row (which are made 0) and the MFknapsack function is called. The function
checks whether the entry v[i][j] is -1 (by checking whether v[i][j] < 0), and if so, it goes
ahead to compute it. If v[i][j] is not -1, then it indicates that this value has already been
computed, and therefore is not computed again but its value is just used. This happens
recursively and the very first value for which this check happens is for v[n][C].
 max( v[i − 1][ j ], vi + v[i − 1][ j − wi ]) if j − wi ≥ 0
The equation v[i ][ j ] = 
v[i − 1][ j ] if j − wi < 0
that has been derived in the Levitin book (Page 295, Eq 8.12), has been used here recursively,
i.e., v[i-1][j] and v[i-1][j-w[i]] are computed recursively only if not computed before. The
value of v[n][C] is returned to the main program.
The program also has to say which are the items actually put into the knapsack so as to
get the optimal solution. The code starts by looking at the value v[n][C]. It compares this with
th
v[n-1][C] and if these values are same, then the i item was not picked up since there was not
enough space in the knapsack. If not same, then the i th item was picked up, and the code resume
searching for other picked items from v[n-1][C-w[i]], since the knapsack now has the i th item
and its remaining capacity is only C-w[i].
The running time of this code is O (nC ) , since the code has to populate the table of that
size.

BNMIT ISE DEPT.


5th semester ADA lab 18

PROGRAM 7: From a given vertex in a weighted connected graph, find shortest paths to other
vertices using Dijkstra’s algorithm.

#include <stdio.h>
#include <conio.h>

void dijkstra(int src, int cost[][10], int dist[], int n)


{
int visited[10],min,i,j,u;
for(i=1;i<=n;i++)
{ visited[i]=0;
dist[i]=cost[src][i];
}
visited[src]=1, dist[src]=0;
for(i=1;i<=n;i++)
{ if (i == src) continue;
min=999;
for(j=1;j<=n;j++)
if ((visited[j] == 0) && (min > dist[j]))
{ min=dist[j];
u=j;
}
visited[u]=1;
for(j=1;j<=n;j++)
if(visited[j] == 0)
{ if(dist[j] > dist[u] + cost[u][j])
dist[j] = dist[u]+cost[u][j];
}
}
}

void main()
{ int n,cost[10][10],dist[10]={0},i,j,src;
clrscr();
printf("enter number of vertices in the graph\n");
scanf("%d",&n);
printf("enter the adjacency matrix of the graph, with 999 for
infinity\n");
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
scanf("%d",&cost[i][j]);
printf("enter the source vertex\n");
scanf("%d",&src);
dijkstra(src,cost,dist,n);
printf("the shortest paths from src %d to all other vertices
are\n",src);
for(i=1;i<=n;i++) printf("to %d = %d\n",i,dist[i]);
getch();
}

This problem, also called Single source shortest path, is an example for Greedy
technique. The function dijkstra starts at a source vertex, which is passed to this function from
the main function. A vector called dist[i], which represents the distance from source vertex to
vertex i, is initialized using the cost matrix from the main function. A vector called

BNMIT ISE DEPT.


5th semester ADA lab 19

visited[i] stores either 1 or 0, where 1 implies that vertex i has been visited and its shortest
path from source vertex has already been computed.
Using the dist vector, the vertex that is closest to the source vertex is found, by being
greedy. Using this vertex, it is checked to see whether any other vertices can come closer to the
source vertex. In this way, at program termination, the shortest distance from the source to all
other vertices is got.
This code runs in O (n 2 ) time because the cost matrix which is to be read has size O (n 2 )
and there are two for loops in the dijkstra function.

BNMIT ISE DEPT.


5th semester ADA lab 20

PROGRAM 8: Sort a given set of elements using Quick sort method and determine the time
required to sort the elements. Repeat the experiment for different values of n , the number of
elements in the list to be searched and plot a graph of the time taken versus n .

#include <stdio.h>
#include <conio.h>

int partition(int a[],int low, int high)


{ int pivot,i,j,temp;
pivot=a[low], i=low, j=high;
while (i<j)
{ while ((a[i]<= pivot) && (i<= high)) i++;
while ((a[j]> pivot) && (j>= low)) j--;
if (i<j)
{ temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
temp=a[j];
a[j]=a[low];
a[low]=temp;
return j;
}

void quicksort(int a[], int low, int high)


{ int j;
if (low<high)
{ j=partition(a,low,high);
quicksort(a,low,j-1);
quicksort(a,j+1,high);
}
}

void main()
{ int i,n,a[10];
clrscr();
printf("enter the number of numbers to sort\n");
scanf("%d",&n);
printf("enter the numbers to sort\n");
for(i=1;i<=n;i++) scanf("%d",&a[i]);
quicksort(a,1,n);
printf("the sorted list is\n");
for(i=1;i<=n;i++) printf("%d\n",a[i]);
getch();
}

This problem is an example under Divide and Conquer strategy. In the general Quicksort,
a pivot element is selected by some strategy. In this code here, the first element of the array sent
to the (sub)problem is selected as pivot and also as a[low], and the last element as a[high].
Two variables i and j are initialized to low and high respectively. As long as pivot is greater
that a[i], i is incremented. As long as pivot is smaller than a[j], j is decremented. Once i and
j stop incrementing and decrementing respectively, they are compared with each other. If i<j,
then a[i] and a[j] are exchanged and comparisons of pivot with a[i] and a[j] are continued

BNMIT ISE DEPT.


5th semester ADA lab 21

as before. If not, then a[j] is exchanged with pivot element. This divides the array between low
and high into 2 parts, i.e., elements smaller than pivot to the left and elements greater than
pivot to the right. The pivot itself is put into the correct sorted position, so it is not touched
again. This creates two subproblems, one with elements from low to the position previous to the
sorted pivot element position, and, another one with elements from position next to the sorted
pivot element position to high.
Finding the sorted pivot element position is done by function partition. Dividing the
array into two parts based on the sorted pivot element position is done by function quicksort
recursively.
The performance of Quicksort depends on the selection of the pivot element. A well-
selected pivot element divides the problem into roughly two halves, because of which the
performance of Quicksort is similar to that of Merge sort, i.e., O (n log n) . A badly selected pivot
element divides the array into two parts, where the number of elements on one side is too small
when compared to that on the other side. In the technique used here, selecting a[low] as pivot
performs very badly when the array is already (or almost) sorted, either in increasing or
decreasing order, because it divides an array of size n into an array of size 1 and an array of size
n − 1 . In this case, Quick sort executes in O (n 2 ) time, just like Insertion sort.

BNMIT ISE DEPT.


5th semester ADA lab 22

PROGRAM 9: Find minimum cost spanning tree of a given undirected graph using Kruskal’s
algorithm.

#include <stdio.h>
#include <conio.h>

void kruskal(int cost[][10], int n, int mst[][10])


{ int min_edge_weight=999,u,v,i,j,k,l,parent[10],edges=0,temp;
for(i=1;i<=n;i++)
{ cost[i][i]=999;
for(j=1;j<=n;j++)
if ((j>i) && (cost[i][j] != 999)) edges++;
}
for(i=1;i<=n;i++) parent[i]=i;
while (edges != 0)
{ for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if ((j>i) && (cost[i][j] < min_edge_weight))
{ min_edge_weight=cost[i][j];
u=i, v=j;
}
i=parent[u], j=parent[v];
if (i != j)
{ mst[u][v]=mst[v][u]=cost[u][v];
if (i<j)
{ for(k=1;k<=n;k++)
if (parent[k] == j) parent[k]=i;
}
else
{ for(k=1;k<=n;k++)
if (parent[k] == i) parent[k]=j;
}
}
cost[u][v]=cost[v][u]=999;
edges--;
min_edge_weight=999;
}
}

void main()
{ int i,j,n,cost[10][10],mst[10][10]={0},total=0;
clrscr();
printf("enter number of vertices in the undirected graph\n");
scanf("%d",&n);
printf("enter the cost matrix of the graph, with 999 for no edges\n");
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
scanf("%d",&cost[i][j]);
kruskal(cost,n,mst);
printf("a MST for the given graph is\n");
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if ((j>i) && (mst[i][j] != 0))
{ printf("%d - %d\n",i,j);
total=total+mst[i][j];
}

BNMIT ISE DEPT.


5th semester ADA lab 23

for(i=1;i<=n;i++)
{ for(j=1;j<=n;j++)
printf("%d\t",mst[i][j]);
printf("\n");
}
printf("the total cost of this MST is %d\n",total);
getch();
}

This is a problem that falls into the Greedy technique category. In the Kruskal’s
algorithm, the edges are first sorted in ascending order of their weights. Each edge is considered
in the sorted order. An edge is added to the growing MST if introducing that edge does not create
a cycle. The algorithm stops when all the edges have been considered.
In the function kruskal here, instead of actually creating an edge list and sorting them
based on their weights, the minimum cost edge is found out each time by going through the
entire cost matrix. This is inefficient as far as the running time is concerned, but it simplifies the
code quite a bit. To find the minimum cost edge using the cost matrix, cost[i][i] is first made
infinity (here, infinity is taken as 999). This is because, for graphs whose vertices do not have
self loops, the cost[i][i] is always 0, and if this is not made infinity, edge i-i will be reported
as the minimum cost edge each time.
The function kruskal stops after considering all edges for inclusion into the MST, so the
number of edges needs to be determined so as to serve as an upper limit to the number of times
the main for-loop is executed. The input being an undirected graph, the entries will be identical
for cost[i][j] and cost[j][i], so only one of these should be considered for counting the
number of edges. Only cost matrix entries which are not infinity should be counted, since those
with infinity represent the lack of edges between corresponding vertices.
The function next finds the currently smallest edge by looking at all the entries of the
cost matrix. The two vertices of this lowest cost edge are stored in u and v. Then, it is checked
whether the parent vertices of u and v are the same (this is explained later). If they are, then a
cycle will be created by introducing edge u-v into the MST, and hence edge u-v is not
introduced. If parents of u and v are not same, then it is fine to introduce edge u-v into the MST.
After considering this edge u-v, the entry cost[u][v] or cost[v][u] must not be
included in further considerations, therefore its entry is made 999. Since this particular edge has
been considered, the number of edges decreases by 1.
Regarding comparing parents of vertices: a vector called parent is kept so that
parent[i] gives the parent of vertex i. In the beginning of function kruskal, each vertex is
initialized as its own parent. Now, somewhere during the execution of the function, the following
scenario occurs. Vertex u is connected to some subtree of the future MST, and vertex v is
connected to another subtree of the future MST. Let these two subtrees to not have any vertices
in common (i.e., let them be disjoint, this is allowed in Kruskal’s algorithm, but not in Prim’s
algorithm), because if they do, then the edge u-v cannot be added to the MST. On adding this
edge, the two previously disjoint subtrees join to become a big tree. All the vertices in this new
tree must now be made to have the same parent. This new parent must be the parent of either u or
v. To break this tie, if the parent of u is numerically smaller than the parent of v, then the parent
of all vertices in the new tree is made as u. Viceversa if parent of v is numerically smaller than
the parent of u.

BNMIT ISE DEPT.


5th semester ADA lab 24

The MST itself is represented by a 2D array called mst. Once an edge u-v is introduced
into the MST successfully, array elements mst[u][v] and mst[v][u] are made equal to
cost[u][v]. Scanning this matrix fully will give all edges of the MST, and adding only non-
zero elements in the upper/lower half of this matrix will give the total weight of the MST.
This implementation executes in O (n 4 ) time. The cost matrix, which is input, is of size
O (n 2 ) . Counting the number of edges in the graph takes O (n 2 ) time, since the entries in the
upper/lower half of the cost matrix, of size roughly n 2 / 2 , has to be considered. Each time the
smallest edge is to be determined, these n 2 / 2 entries are looked at. There can be at most n 2 / 2
edges, so the overall running time is roughly n 4 / 4 , which is O (n 4 ) .

BNMIT ISE DEPT.


5th semester ADA lab 25

PROGRAM 10a: Print all the nodes reachable from a given starting node in a digraph using
BFS method.

#include <stdio.h>
#include <conio.h>

void insertq(int q[], int node, int* f, int* r)


{
if ((*f==-1) && (*r==-1))
{ (*f)++, (*r)++, q[*f]=node;
}
else
{ (*r)++, q[*r]=node;
}
}

int deleteq(int q[], int* f, int* r)


{ int temp;
temp=q[*f];
if (*f==*r) *f=*r=-1;
else (*f)++;
return temp;
}

void bfs(int n, int adj[][10], int src, int visited[])


{ int q[20],f=-1,r=-1,v,i;
insertq(q,src,&f,&r);
while ((f <= r) && (f != -1))
{ v=deleteq(q,&f,&r);
if (visited[v]!=1)
{ visited[v]=1;
printf("%d ",v);
}
for(i=1;i<=n;i++)
if ((adj[v][i] == 1) && (visited[i] != 1))
insertq(q,i,&f,&r);
}
}

void main()
{ int n,i,j,adj[10][10],src,visited[10];
clrscr();
printf("enter number of vertices\n");
scanf("%d",&n);
printf("enter the adjacency matrix of the digraph for BFS\n");
for(i=1;i<=n;i++)
{ visited[i]=0;
for(j=1;j<=n;j++)
scanf("%d",&adj[i][j]);
}
printf("enter starting vertex\n");
scanf("%d",&src);
printf("the nodes reachable from src are\n");
bfs(n,adj,src,visited);
getch();
}

BNMIT ISE DEPT.


5th semester ADA lab 26

This problem is an example for Decrease and conquer, i.e., when each vertex is visited,
the number of vertices to be visited decreases by one.
A simple queue is implemented here, with insertion from rear and deletion from front. A
vertex i is first deleted from the queue, visited by the function bfs (i.e., visited[i] is made 1),
and all its unvisited neighbors are inserted into the queue at the rear. This way, the function bfs
visits all vertices in the graph in a `as wide as possible’ manner. As and when the function bfs
visits a vertex, it is printed out. If all the vertices in the graph are not printed out, it does not
necessarily mean that the graph is disconnected. It may only mean that there are no in-edges
going into (but only out-edges coming out of) the vertices that have not been printed.
The running time of this code is O (n 2 ) because an adjacency matrix of size O (n 2 ) has
been used.

BNMIT ISE DEPT.


5th semester ADA lab 27

PROGRAM 10b: Check whether a graph is connected or not using DFS method.

#include <stdio.h>
#include <conio.h>

void dfs(int adjm[][10],int visited[],int n, int src)


{ int w;
visited[src]=1;
for(w=1;w<=n;w++)
{ if ((adjm[src][w] == 1) && (visited[w] == 0))
{ printf("%d -- %d\n",src,w);
dfs(adjm,visited,n,w);
}
}
}

void main()
{ int n,i,j,adjm[10][10],src,visited[10]={0},flag,p;
clrscr();
printf("enter number of nodes for the undirected graph for DFS\n");
scanf("%d",&n);
printf("enter the adjacency matrix\n");
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
scanf("%d",&adjm[i][j]);
printf("enter source vertex from where to start DFS\n");
scanf("%d",&src);
dfs(adjm,visited,n,src);
for(i=1;i<=n;i++)
if (visited[i] != 1) flag=1, p=i;
if (flag == 1) printf("the node %d is isolated and graph is
unconnected\n",p);
else printf("the graph is connected\n");
getch();
}

This is a problem under Decrease and Conquer. The strategy is to visit one vertex at a
time, because of which the number of vertices to be visited reduces by 1.
The dfs function moves from one vertex to another vertex if there is an edge between the
two vertices. When the dfs function visits a vertex x, visited[x] is made 1. If the graph is
connected, then the dfs function is able to visit all the vertices. On checking the visited array
to see whether all the vertices are visited, if the main function sees that some vertex is unvisited
(i.e., if visited[x] = 0 for some x), the graph is reported to be disconnected.
Recursion is ideally suited to implement DFS. Take any recursive function and draw a
tree whose nodes represent every stage of execution of the recursive function. Now, if DFS is
done on this tree, it can be seen that the DFS ordering is identical to the way in which the
recursive function executed and the tree was generated.
The running time of this code is O (n 2 ) because an adjacency matrix of size O (n 2 ) has
been used.

BNMIT ISE DEPT.


5th semester ADA lab 28

PROGRAM 11: Find a subset of a given set S = {s1 , s2 ,L, sn } of n positive numbers whose sum
is equal to a given positive number d . For example, if S = {1,2,5,6,8} and d = 9 , there are 2
solutions {1,2,6} and {1,8} . A suitable message is to be displayed if the given problem instance
does not have a solution.

#include <stdio.h>
#include <conio.h>

int is_even(int i)
{ return (i%2 == 0)?1:0;
}

int power(int x,int y)


{ int i,res=1;
for(i=1;i<=y;i++) res=res*x;
return res;
}

int sos(int n, int s[], int d)


{ int sst[100],i,j,flag,k=1,h;
sst[k]=0, k++,flag=1;
for(i=1;i<=n;i++)
{ for(j=1;j<=power(2,i);j++)
{ if (flag==1)
{ sst[k]=sst[(k/2)]+s[i];
flag=0, k++;
}
else
{ sst[k]= sst[(k/2)];
flag=1, k++;
}
}
}
flag=0;
for(i=power(2,n); i<=(power(2,n+1)-1);i++)
{ j=i, h=n;
if (sst[j] == d)
{ flag=1;
while(h>0)
{ if (is_even(j))
{ printf("%d ",s[h]);
j=j/2, h--;
}
else
{ j=j/2, h--;
}
}
printf("\n");
}
}
return flag;
}

void main()
{ int i,n,s[10],d,flag;

BNMIT ISE DEPT.


5th semester ADA lab 29

clrscr();
printf("enter number of numbers\n");
scanf("%d",&n);
printf("enter the set S of %d numbers\n",n);
for(i=1;i<=n;i++) scanf("%d",&s[i]);
printf("enter d\n");
scanf("%d",&d);
flag=sos(n,s,d);
if (flag==0) printf("no solution\n");
getch();
}

This problem is an example for the Backtracking technique. A set S = {s1 , s2 ,L, sn } of n
integers and another integer d are given, and a subset of set S is to be found such that the
elements of this subset add up to exactly d . Using backtracking technique, this problem is
solved as follows. The element si is considered with the subset {s1 ,L, si−1} to see whether their
sum total exceeds, or is exactly equal to, or is below integer d . If the total exceeds d , then si is
not included into the subset. If the total is exactly d , then si is included into the subset, a
solution is found and it is reported. If the total falls below d , then si is included into the subset
but a solution has not been found yet. This generates a state space tree in which considering each
si creates two states, one with si and one without si . The state space tree will have height n ,
because there are n elements in set S . By searching the leaf level of this tree for d , and by
working upwards to the root of this tree to find out which elements of S were added so as to get
d , the solution to this problem is obtained.
The code for this technique can be written recursively because, at each step the program
has to decide whether to include si or not. The code here solves this program iteratively.
The function sos receives n, array s representing the set S , and integer d from the main
function. In the function sos, the 1D array sst represents the state space tree to be generated.
The root of this tree (i.e., sst[1]) contains 0, since no elements of S have been considered yet.
This root has 2 children, the left child having the total with considering s1 and the right child
having the total without considering s1 . From the point of view of the root whose sst array
index is k=1, the left child is obtained by 2k (i.e., sst[2]) and the right child by 2k+1 (i.e.,
sst[3]). From the point of view of the child (whether right or left), its parent (the root) is
obtained by dividing its sst index number k/2. This idea of parent-child relationship is very
similar to that of a heap, and that is why the index of the root element is starting from 1 and not
from 0.
In the ith level of the tree, the ith element of S is considered. The number of subtotals to
be computed (i.e., the number of nodes) at that level is 2i . To compute 2i and other powers, a
function power has been written and the variable j walks from left to right in that level to
compute the subtotals. A flag variable has been used as a flip-flop because, if one time the
subtotal is calculated with si , the next time it is calculated without si .
After computing the entire tree, the last level elements are looked at. At level 0, the tree
element is accessed from 1 to 1, i.e., 20 to 21 − 1 . At level 1, the tree elements are accessed from
2 to 3, i.e., 21 to 2 2 − 1 . At level 2, the tree elements are accessed from 4 to 7, i.e., 2 2 to 23 − 1 .

BNMIT ISE DEPT.


5th semester ADA lab 30

Similarly, at the last (nth) level, the tree elements are accessed from 2 n to 2 n+1 − 1 . An index j is
used to walk through this list. If there is a solution to the problem, then the integer d will be
available at some position j (i.e., sst[j]) at this level. Now, after finding d in this level, if j is
even, then it means that sst[j] is a left child (left child is 2k and a multiple of 2, hence even),
i.e., the value of sst[j] was calculated by including the nth item, therefore this item must be
printed. If j is odd, then it means that sst[j] is a right child (right child is 2k+1, hence odd),
i.e., the value of sst[j] was calculated without including the nth item.
The task is to get all elements which when added together give d . If sst[j] was
included during adding so as to get d , then its parent is checked to see whether it was also
included in adding so as to get d . This is done by checking whether the parent is an odd child or
an even child of its parent (i.e., sst[j]’s grandparent). In this way, by checking all the way up to
the root of the state space tree, a subset of S whose elements add to d is obtained. There might
be other occurrences of d on the nth level, all of which give valid solutions to this problem.
This algorithm takes running time proportional to the size of the state space tree. In the
zeroth level, there are 20 nodes. In the first level, there are 21 nodes. In the nth level, there are
2 n nodes. On adding all this, we have 20 + 21 + 2 2 + L + 2 n = 2 n+1 = O (2 n ) .

BNMIT ISE DEPT.


5th semester ADA lab 31

PROGRAM 12a: Implement Horspool algorithm for String Matching.

#include <stdio.h>
#include <conio.h>
#include <string.h>

void shifttable(char p[], int m, int table[])


{ int j;
for(j=0;j<=255;j++) table[j] = m;
for(j=0;j<=m-2;j++) table[p[j]] = m-1-j;
}

int horspool(char t[], int n, char p[], int m)


{ int i, k, table[300];
shifttable(p,m,table);
i=m-1;
while (i <= n-1)
{ k=0;
while ((k <= m-1) && (p[m-1-k] == t[i-k])) k++;
if (k==m) return i-m+1;
else i=i+table[t[i]];
}
return -1;
}

void main()
{ char t[100], p[15],temp;
int m,n,ret=-1,i;
clrscr();
printf("enter the main string in which to search\n");
gets(t);
n=strlen(t);
printf("enter the pattern string\n");
gets(p);
m=strlen(p);
ret=horspool(t,n,p,m);
if (ret == -1) printf("search failed\n");
else printf("pattern found at position %d\n",ret);
getch();
}

This problem uses a technique called Input enhancement, where the input is preprocessed
so that queries run faster. The search pattern of length m is processed and a table called shift-table
is created by the shifttable function. This table has an integer shift value for all characters in
the alphabet. For characters which are not in the pattern, their entries just contain the length of
the pattern. For characters which are in the pattern, their entries contain the distance from the
character’s rightmost occurrence to the rightmost character in the pattern. No shift-table entry
will have 0 or negative values.
In the function horspool, the pattern is aligned to the beginning of the text which has
length n. The string matching starts by comparing the last character of the pattern p[m-1] with
the character of the text t[i] against which it is aligned. If these two characters are same, then
the previous characters in both the pattern and the text are compared (i.e., p[m-2] and t[i-1]).
This goes on either till the full pattern exactly matches the text’s characters, or until an

BNMIT ISE DEPT.


5th semester ADA lab 32

unmatching character pair is found. When this happens, the first compared character t[i] in the
text is looked up in the shift-table and the pattern jumps by the amount specified there. This
continues as long as the text is not finished.
There are two while loops in the function horspool. The outer one has i running from m-
1 to n-1, i.e., at most n-m-1 times. The inner one has k running from 0 to m-1, i.e., at most m
times. On multiplying these two terms, we get O (nm) .

BNMIT ISE DEPT.


5th semester ADA lab 33

PROGRAM 12b: Find the Binomial Co-efficients using Dynamic Programming.

#include <stdio.h>
#include <conio.h>

void bincoeff(int a[][50],int n)


{ int i,j;
for(i=0;i<=n;i++)
{ a[i][0]=1;
a[i][i]=1;
}
for(i=2;i<=n;i++)
for(j=1;j<=i-1;j++)
a[i][j] = a[i-1][j] + a[i-1][j-1];
}

void main()
{ int n,a[50][50],i,j;
clrscr();
printf("enter n, where coefficients of (a+b)^n are required\n");
scanf("%d",&n);
bincoeff(a,n);
printf("the coefficients of (a+b)
^%d are ",n);
for(i=0;i<=n;i++) printf("%d ",a[n][i]);
getch();
}

This problem is an example for dynamic programming technique. Binomial coefficients


are the numerical coefficients of the expansion of (a + b) n . For eg., for power 2, the numerical
coefficients are 1, 2 and 1, because on expanding (a + b) 2 , we get a 2 + 2ab + b 2 . This is exactly
the same as computing a row (3rd row) of the Pascal triangle.
Dynamic programming is used here because of the following property in Pascal’s
triangle, which is stored in the 2D array a: any entry a[i][j] can be computed by adding a[i-
1][j] and a[i-1][j-1]. This computation stops when the base cases occur, and this happens
when either or both indices become 0. This is because the numerical coefficients of both a n and
b n in the expansion of (a + b) n , for any n , are 1.
The running time of this code is the time that is taken to generate Pascal’s triangle.
Expansion of (a + b) 0 has 1 coefficient, i.e. 1. Expansion of (a + b)1 has 2 coefficients, i.e., 1
and 1. Expansion of (a + b) 2 has 3 coefficients. Similarly, expansion of (a + b) n has n + 1
coefficients. All of these have to be generated using bottom-up dynamic programming, so on
(n + 1)(n + 2)
adding 1 + 2 + 3 + L + (n + 1) , we get , which works out to O (n 2 ) .
2

BNMIT ISE DEPT.


5th semester ADA lab 34

PROGRAM 13: Find the Minimum Cost Spanning Tree of a given undirected graph using
Prim’s algorithm.

#include <stdio.h>
#include <conio.h>

void prim(int cost[][10], int n, int mst[][10])


{ int visited[10]={0}, min_edge_weight=999,u,v,i,j,vertices=0;
visited[1]=1;
vertices++;
while (vertices < n)
{ for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{ if ((visited[i] == 1) && (visited[j] != 1) &&
(cost[i][j] < min_edge_weight))
{ min_edge_weight = cost[i][j];
u=i,v=j;
}
}
visited[v]=1;
vertices++;
mst[u][v]=mst[v][u]=min_edge_weight;
min_edge_weight=999;
}
}

void main()
{ int i,j,n,cost[10][10],mst[10][10]={0},total=0;
clrscr();
printf("enter number of vertices in the undirected graph\n");
scanf("%d",&n);
printf("enter the cost matrix of the graph, with 999 for no edges\n");
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
scanf("%d",&cost[i][j]);
prim(cost,n,mst);
printf("a MST for the given graph is\n");
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if ((j>i) && (mst[i][j] != 0))
{ printf("%d - %d\n",i,j);
total=total+mst[i][j];
}
for(i=1;i<=n;i++)
{ for(j=1;j<=n;j++)
printf("%d\t",mst[i][j]);
printf("\n");
}
printf("the total cost of this MST is %d\n",total);
getch();
}

This problem is an example for the Greedy technique. The program starts with some
vertex and finds out the smallest edge coming out of that vertex so that, one vertex of that edge is
already visited and the other vertex of that edge is not visited. In some ith iteration of the Prim’s

BNMIT ISE DEPT.


5th semester ADA lab 35

algorithm, the graph is divided into 2 parts, where one part is the component in which all vertices
are visited, and another part is the component in which all vertices are unvisited. In this scenario,
an edge is to be picked up so that its weight is minimum among all unpicked edges, and this edge
goes from a vertex in the visited component to another vertex in the unvisited component.
Once this edge is added to the MST, the unvisited vertex is made visited and therefore, it
joins the visited component. A 2D array called mst stores the weight of the edge u-v in
mst[u][v], so that this information is available to the main function. The function stops when
all the vertices have been visited.
To find the minimum edge each time, the function prim looks at the upper half of the
matrix cost (as the input graph is undirected, the upper and lower halves of cost matrix are
identical). This takes time roughtly n 2 / 2 . This is done once for each vertex, and there are n
vertices, so the overall running time of this implementation is O (n 3 ) .

BNMIT ISE DEPT.


5th semester ADA lab 36

PROGRAM 14b: Compute the transitive closure of a given directed graph using Warshall’s
algorithm.

#include <stdio.h>
#include <conio.h>

void warshal(int adjm[][10], int n)


{ int i,j,k;
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
adjm[i][j] = adjm[i][j] || (adjm[i][k] &&
adjm[k][j]);
}

void main()
{ int n, i,j,adjm[10][10];
clrscr();
printf("enter the number of vertices in the given digraph\n");
scanf("%d",&n);
printf("enter the adjacency matrix of graph\n");
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
scanf("%d", &adjm[i][j]);
warshal(adjm,n);
printf("the transitive closure of entered digraph\n");
for(i=1;i<=n;i++)
{ for(j=1;j<=n;j++)
printf("%d ",adjm[i][j]);
printf("\n");
}
getch();
}

This program is an example for Dynamic programming, and the code is similar to that of
Floyd’s algorithm (Program 5b), but the functionality of these two programs are very different.
Warshall’s algorithm computes the transitive closure of a graph. In the transitive closure of a
given graph, there is a 1 between vertices u and v if there is a path (direct edge or a multi-edged
path) between u and v.
If there is an edge/path between u and some intermediate vertex t, and, if there is an
edge/path between t and v, then there is a path between u and v. To find this, all vertices other
than u and v need to be put in the place of t and checked. This is done for all combinations of u
and v. The matrix adjm itself is modified, if need be, each time a new vertex is considered for t.
There are nC2 combinations of 2 vertices among n vertices. Each combination is checked
with all other n − 2 vertices as intermediate vertices. This works out to O (n 3 ) .

BNMIT ISE DEPT.


5th semester ADA lab 37

PROGRAM 15: Implement n-queens problem using Back tracking.

#include <stdio.h>
#include <conio.h>

int x[20]={0};

int abs(int y)
{ int i=0;
if (y>=0) return y;
else
{ while(y<=0) { y++, i++; }
return --i;
}
}

int place(int k, int i)


{ int j;
for(j=1;j<=k-1;j++)
if ((x[j] == i) || (abs(x[j]-i) == abs(j-k))) return 0;
return 1;
}

void nqueens(int k, int n)


{ int i,j;
for(i=1;i<=n;i++)
{ if (place(k,i))
{ x[k]=i;
if (k==n)
{ for(j=1;j<=n;j++) printf("%d ",x[j]);
printf("\n");
}
else nqueens(k+1,n);
}
}
}

void main()
{ int n;
clrscr();
printf("enter the number of queens to place\n");
scanf("%d",&n);
nqueens(1,n);
getch();
}

An n × n chessboard and n queens are given. All n queens are to be placed on the given
chessboard so that no queen cancels out any other queen. Queens cancel other pawns (including
other queens) horizontally, vertically and diagonally. This problem can be solved using the
Backtracking technique.
The array x is made global and this is the solution vector, i.e., if the 3rd queen is placed in
column 2 of the third row, then x[3]=2. It should also be noted that a kth queen is always placed
in the kth row and the program has to decide upon which column among the n columns of the kth
row to place it in.

BNMIT ISE DEPT.


5th semester ADA lab 38

The function place takes in two values, k and i, and returns 1 if kth queen can be placed
in column i, otherwise returns 0. It checks this by seeing whether i is the same as a column in
which an earlier queen was placed, because if so, then the kth queen cannot be placed in that
column (check for vertical cancellation). It also checks whether the cell at the kth row and the ith
column is in the same diagonal as any previously placed queen, because if so, then the kth queen
cannot be placed at that particular cell (check for diagonal cancellation). There is no need to
check for horizontal cancellation, because no 2 queens are kept in the same row.
The function nqueens gets either 1 or 0 from place function and if the return value is 1,
the k queen is placed in the ith column. If not, then a queen cannot be placed in that column, so
th

the next (i.e., k+1th) queen is considered. If k=n, then the nth queen was successfully placed into a
column, which means we have a solution to the n -queens problem, so the solution vector is
reported.
This code generates an implicit state space tree. For the 4-queens problem, the number of
nodes of the state space tree is as follows. At the zeroth level, there is only one, i.e., 40 , node. At
the first level, there are 4, i.e., 41 , nodes, and in the next level, each of these 4 nodes will have 4
nodes each (of course, many of these nodes do not represent solutions), so at the second level,
there are 16, i.e., 4 2 , nodes. At the 4th level (which is the n th level for 4-queens problem), there
are 4 4 nodes.
Similarly, for the n -queens problem, the number of nodes at the zeroth level is n 0 . At the
first level, it is n1 . At the second level, it is n 2 . So at the n th level, it is n n . On adding all these
n n+1 − 1
number of nodes at each level, we get n 0 + n1 + n 2 + L + n n , which works out to , which is
n −1
O (n n ) .

BNMIT ISE DEPT.

Você também pode gostar