+ All Categories
Home > Documents > Algorithmics and Programming 2

Algorithmics and Programming 2

Date post: 24-Jan-2022
Category:
Upload: others
View: 10 times
Download: 0 times
Share this document with a friend
41
Algorithmics and Programming 2 Exercises from the Theory Lessons May 31, 2021 This is a compilation of all problems solved during the course of Algorithmics and Programming 2, which took place during Spring 2021. The present solutions are proposed by students, each credited with their corresponding author. . Abstract Data Types 1. Implement methods. Implement the following methods for the class Rectangle: // Rotate the rectangle 90 degrees clockwise or // counterclockwise, depending on the value of the parameter. // The rotation should be done around the lower-left corner // of the rectangle. void rotate(bool clockwise); // Flip horizontally (around the left edge) or vertically (around // the bottom edge), depending on the value of the parameter. void flip(bool horizontally); // Check whether point p is inside the rectangle bool isPointInside(const Point& p) const; Solution: void rotate(bool clockwise) { swap(w, h); ll = clockwise ? Point(ll.getX(), ll.getY() - w) : Point(ll.getX() - h, ll.getY()); } void flip(bool horizontally) { ll = horizontally ? Point(ll.getX() - w, ll.getY()) : Point(ll.getX(), ll.getY() - h); } bool isPointInside(const Point& p) const { return (ll.getX() <= p.getX() and p.getX() <= ll.getX() + w and ll.getY() <= p.getY() and p.getY() <= ll.getY() + h); } Author: Adri`a Di´ eguezMoscard´o
Transcript
Page 1: Algorithmics and Programming 2

Algorithmics and Programming 2

Exercises from the Theory Lessons

May 31, 2021

This is a compilation of all problems solved during the course of Algorithmics and Programming 2,which took place during Spring 2021. The present solutions are proposed by students, each credited withtheir corresponding author.

.

Abstract Data Types

1. Implement methods. Implement the following methods for the class Rectangle:

// Rotate the rectangle 90 degrees clockwise or

// counterclockwise, depending on the value of the parameter.

// The rotation should be done around the lower-left corner

// of the rectangle.

void rotate(bool clockwise);

// Flip horizontally (around the left edge) or vertically (around

// the bottom edge), depending on the value of the parameter.

void flip(bool horizontally);

// Check whether point p is inside the rectangle

bool isPointInside(const Point& p) const;

Solution:

void rotate(bool clockwise)

swap(w, h);

ll = clockwise ? Point(ll.getX(), ll.getY() - w) :

Point(ll.getX() - h, ll.getY());

void flip(bool horizontally)

ll = horizontally ? Point(ll.getX() - w, ll.getY()) :

Point(ll.getX(), ll.getY() - h);

bool isPointInside(const Point& p) const

return (ll.getX() <= p.getX()

and p.getX() <= ll.getX() + w

and ll.getY() <= p.getY()

and p.getY() <= ll.getY() + h);

Author: Adria Dieguez Moscardo

Page 2: Algorithmics and Programming 2

2. Re-implement a class. Re-implement the class Rectangle using an internal representation withtwo Points: LowerLeft (LL) and UpperRight (UR).

Solution: First, we declare the class in the headers file with the appropriate methods. We skipdeclarations that remain unchanged.

class Rectangle

public:

Rectangle(const Point& ll, const Point& ur);

...

private:

Point ll, ur;

double getW() const; // get Width

double getH() const; // get Height

;

#endif

Now, implement each individual method using the new internal representation.

// Constructors

// Empty rectangle

Rectangle::Rectangle() : Rectangle(Point(), Point())

// Construct rectangle from LL and UR points.

Rectangle::Rectangle(const Point& ll, const Point& ur) :

ll(ll), ur(ur)

// Construct rectangle from a point, width and height.

Rectangle::Rectangle(const Point& P, double w, double h) :

ll(P), ur(P.getX() + w , P.getY() + h)

// Construct a point at the origin, with given width and height.

Rectangle::Rectangle(double w, double h) :

ll(Point(0,0)), ur(Point(w,h))

// Private Methods

double Rectangle::getW() const

return ur.getX() - ll.getX();

double Rectangle::getH() const

return ur.getY() - ll.getY();

// Public Methods :

double Rectangle::area() const

return getW()*getH();

Page 2 of 41

Page 3: Algorithmics and Programming 2

void Rectangle::scale(double s)

ll = Point(ll.getX() * s, ll.getY() * s);

ur = Point(ur.getX() * s, ur.getY() * s);

void Rectangle::rotate(bool clockwise)

if(clockwise)

ur = Point(ll.getX() + getH(), ll.getY());

ll = Point(ll.getX(), ll.getY() - getW());

else

ur = Point(ll.getX(), ll.getY() + getW());

ll = Point(ll.getX() - getH(), ll.getY());

void Rectangle::move(const Point& A)

ll = Point(ll.getX() + A.getX(), ll.getY() + A.getY());

ur = Point(ur.getX() + A.getX(), ur.getY() + A.getY());

void Rectangle::flip(bool horizontally)

if(horizontaly)

ur = Point(ll.getX(), ur.getY());

ll = Point(ll.getX() - getW(), ll.getY());

else

ur = Point(ur.getX(), ll.getY());

ll = Point(ll.getX(), ll.getY() - getH());

bool Rectangle::isPointInside(const Point& A) const

return A.getX() >= ll.getX() and A.getX() <= ur.getX()

and A.getY() >= ll.getY() and A.getY() <= ur.getY();

Rectangle& Rectangle::operator *= (const Rectangle& R)

ll = Point(max(ll.getX(), R.ll.getX()),

max(ur.getY(), R.ur.getY()));

ur = Point(min(ur.getX(), R.ur.getX()),

min(ur.getY(), R.ur.getY()));

return *this;

Rectangle Rectangle::operator * (const Rectangle& R) const

Rectangle result = *this;

result *= R;

return result;

Author: Eva Brigitte Fernandez Romero

Page 3 of 41

Page 4: Algorithmics and Programming 2

Algorithm Analysis

3. For Loops.

Analyze the cost of each code. Use the following equalities:

n∑i=1

i =n(n + 1)

2

n∑i=1

i2 =n(n + 1)(2n + 1)

6

// Code 1

int s = 0;

for (int i = 0; i < n; ++i) ++s;

Solution:n−1∑i=0

1 = n ∈ Θ(n)

// Code 2

int s = 0;

for (int i = 0; i < n; i += 2) ++s;

Solution:dn

2 e∑i=0

1 =⌈n

2

⌉∈ Θ(n)

// Code 3

int s = 0;

for (int i = 0; i < n; ++i) ++s;

for (int j = 0; j < n; ++j) ++s;

Solution:n−1∑i=0

1 +

n−1∑i=0

1 = 2n ∈ Θ(n)

// Code 4

int s = 0;

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

for (int j = 0; j < n; ++j) ++s;

Solution:n−1∑i=0

n−1∑j=0

1 =

n−1∑i=0

n = n2 ∈ Θ(n2)

Page 4 of 41

Page 5: Algorithmics and Programming 2

// Code 5

int s = 0;

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

for (int j = 0; j < i; ++j) ++s;

Solution:n−1∑i=0

i−1∑j=0

1 =

n−1∑i=0

i =n(n− 1)

2∈ Θ(n2)

// Code 6

int s = 0;

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

for (int j = i; j < n; ++j) ++s;

Solution:n−1∑i=0

n−1∑j=i

1 =

n−1∑i=0

n− i =n(n + 1)

2∈ Θ(n2)

// Code 7

int s = 0;

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

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

for (int k = 0; k < n; ++k) ++s;

Solution:n−1∑i=0

n−1∑j=0

n−1∑k=0

1 =

n−1∑i=0

n2 = n3 ∈ Θ(n3)

// Code 8

int s = 0;

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

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

for (int k = 0; k < j; ++k) ++s;

Page 5 of 41

Page 6: Algorithmics and Programming 2

Solution:

n−1∑i=0

i−1∑j=0

j−1∑k=0

1 =

n−1∑i=0

i(i− 1)

2=

=1

2

n−1∑i=0

i2 − 1

2

n−1∑i=0

i =n(n− 1)(2n− 1)

12− n(n− 1)

4=

=n3 − 3n2 + 2n

6∈ Θ(n3)

// Code 9

int s = 0;

for (int i = 1; i <= n; i *= 2) ++s;

Solution:blog2 nc+1∑

i=1

1 = blog2 nc+ 1 ∈ Θ(log n)

// Code 10

int s = 0;

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

for (int j = 0; j < i*i; ++j)

for (int k = 0; k < n; ++k) ++s;

Solution:n−1∑i=0

i2−1∑j=0

n−1∑k=0

1 = n

n−1∑i=0

i2 =n2(n− 1)(2n− 1)

6∈ Θ(n4)

// Code 11

int s = 0;

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

for (int j = 0; j < i*i; ++j)

if (j%i == 0)

for (int k = 0; k < n; ++k) ++s;

Solution: Here, it is important to notice that the second loop runs only when i divides j, thatis, j = ri where 0 ≤ r < i.

n−1∑i=0

i−1∑r=0

n−1∑k=0

1 = n

n−1∑i=0

i =n2(n− 1)

2∈ Θ(n3)

Author: Aina Perez Rodas

Page 6 of 41

Page 7: Algorithmics and Programming 2

4. Asymptotic notation.

The following statements refer to the insertion sort algorithm and the X’s hide an occurrence of O,Ω or Θ. For each statement, find which options for X ∈ O,Ω,Θ make the statement true or false.Justify your answers.

1. The worst case is X(n2).

2. The worst case is X(n).

3. The best case is X(n2).

4. The best case is X(n).

5. For every probability distribution, the average case is X(n2).

6. For every probability distribution, the average case is X(n).

7. For some probability distribution, the average case is X(n log n).

Solution:

1. O, Ω i Θ fan l’afirmacio certa. El cas pitjor es dona, per exemple, quan el vector estaordenat al reves.

2. Nomes Ω(n) fa l’afirmacio certa, ates que el cas pitjor es Θ(n2).

3. Nomes Ofa l’afirmacio certa.

4. O, Ω i Θ fan l’afirmacio certa. El cas millor es dona quan el vector esta ordenat.

5. Nomes es certa per O. El cas mitja sempre es Odel cas pitjor. Hi ha probabilitats dedistribucio que fan que els cas mitja sigui Θ(n) (per vector ordenats, per exemple) i, pertant, no podrien ser Ω(n2).

6. Nomes es certa per Ω. El cas mitja sempre es Ω del cas pitjor. Hi ha probabilitats dedistribucio que fan que els cas mitja sigui Θ(n2) (per vector ordenats al reves, per exemple)i, per tant, no podrien ser O(n2).

7. O, Ω i Θ fan l’afirmacio certa. Podem trobar una distribucio de probabilititat que facique l’ordenacio sigui Θ(n log n). Per exemple, considerem tots els vectors de la forma[x1, . . . , xp, y1, . . . , yq] tal que p + q = n, q = log2 n, [x1, . . . , xp] esta ordenat i [y1, . . . , yq]tambe esta ordenat, amb yq < x1. En aquest cas caldria traslladar els elements y a l’inicidel vector. El cost seria:

Θ(n− log n) + Θ((log n) · (n− log n)) = Θ(n log n).

Basat en la solucio proposada per Armand De Asıs Molleja

Page 7 of 41

Page 8: Algorithmics and Programming 2

5. Primality. The following algorithms try to determine whether n ≥ 0 is prime. Find which ones arecorrect and analyze their cost as a function of n.

bool isPrime1(int n)

if (n <= 1) return false;

for (int i = 2; i < n; ++i) if (n%i == 0) return false;

return true;

bool isPrime2(int n)

if (n <= 1) return false;

for (int i = 2; i*i < n; ++i) if (n%i == 0) return false;

return true;

bool isPrime3(int n)

if (n <= 1) return false;

for (int i = 2; i*i <= n; ++i) if (n%i == 0) return false;

return true;

bool isPrime4(int n)

if (n <= 1) return false;

if (n == 2) return true;

if (n%2 == 0) return false;

for (int i = 3; i*i <= n; i += 2) if (n%i == 0) return false;

return true;

Solution:

isPrime1

Codi : El codi es correcte, mirem desde el primer nombre primer fins a n− 1 si algun numero eldivideix.

Eficiencia: Trobem un bucle de 2 fins a n, per tant podem dir que la eficiencia es O(n), es adir, lineal.

isPrime2

Codi : El codi es incorrecte, mirem desde el primer nombre primer fins a√n− 1 si algun numero

el divideix. El cas que donaria per valid quan no ho es, per exemple, seria n = 9, on noconsideraria el i = 3. Diria que es primer quan no ho es.

Eficiencia: Trobem un bucle de 2 fins a√n, per tant podem dir que la eficiencia es O(

√n), es

a dir, sub-lineal.

isPrime3

Codi : El codi es correcte, mirem desde el primer nombre primer fins a√n si algun numero el

divideix (aplicant un teorema que ens diu que unicament ens fa falta comprovar-ho fins arrel den).

Eficiencia: Trobem un bucle de 2 fins a√n, per tant podem dir que la eficiencia es O(

√n), es

a dir, sub-lineal.

isPrime4

Page 8 of 41

Page 9: Algorithmics and Programming 2

Codi : El codi es correcte, apliquem una mica de precalcul, si trobem algun multiple de dos eldescartem(ja no seria primer). Mirem desde el primer nombre primer fins a O(

√n) (de dos en

dos) y mirem si algun numero el divideix.

Eficiencia: Trobem un bucle de 2 fins a n saltant de dos en dos. Els casos es redueixen a lameitat, pero com a la notacio O ignorem les constants, podem dir que la eficiencia es O(

√n).

Author: Armand De Asıs Molleja

6. The Sieve of Eratosthenes. The following program is a version of the Sieve of Eratosthenes.Analyze its complexity.

vector<bool> Primes(int n)

vector<bool> p(n + 1, true);

p[0] = p[1] = false;

for (int i = 2; i*i <= n; ++i)

if (p[i])

for (int j = i*i; j <= n; j += i) p[j] = false;

return p;

You can use the following equality, where p ≤ x refers to all primes p ≤ x:∑p≤x

1

x= log log x + O(1).

Solution: Fem el canvi de variable j = ki; k = i...⌊ni

⌋.

∑i≤√n

ni∑

k=i

1 =∑i≤√n

(ni− i + 1

)=

= n∑i≤√n

(1

i− i

n+

1

n

)= n

∑i≤√n

1

i+

1

n

∑i≤√n

(1− i).

El segon sumatori clarament pertany a Obigoh(n) i fins i tot mes petit. Tenim doncs, fent servir la igualtat de l’enunciat, que l’expressioanterior

= n log log√n + O(1) + O(n) = O(n log log

√n) = O(n log log n).

Author: Armand De Asıs Molleja

Page 9 of 41

Page 10: Algorithmics and Programming 2

7. Cell Phone Dropping Problem.

Trabajamos para una empresa que acaba de inventar un nuevo protector de movil y quiere anunciarque se puede caer desde el piso f sin romperse.

Tenemos 1, 2 moviles y un edificio de n pisos, hemos de proponer un algoritmo que minimice elnumero de pruebas para saber el piso mas alto en el cual no se rompera.

Solution:

Caso 1: un movil

En este caso no hay mas remedio que empezar lanzando el movil por el primer piso e ir probandode uno en uno. En el peor caso haremos n intentos, donde n es el numero de pisos.

Caso 2: dos moviles

En este caso no es tan sencillo. Intentemos resolverlo analıticamente. Supongamos que se puederesolver en x intentos, entonces el primer piso que tenemos que probar es el numero x. Si elmovil se rompe entonces necesitaremos x − 1 intentos mas. En caso de que no se rompa lotiramos del piso x + (x− 1). Si se rompe necesitaremos, x− 2 intentos mas. Si no, lo lanzamosdel piso x + (x− 1) + (x− 2), y ası sucesivamente hasta haber probado todos los pisos.

Por tanto, para que el problema se pueda resolver en x intentos, es necesario que se cumpla que

x + (x− 1) + (x− 2) + ... + 1 ≥ n.

Como buscamos el numero mınimo, la desigualdad se convierte en igualdad, y obtenemos

x(x− 1)

2= n

Por lo tanto x = −1±√−1+8n2 , donde x es el numero de intentos que nos llevara como maximo y

n el numero de pisos.

En caso de obtener un resultado no entero, redondeamos hacia arriba. x sera el primer piso queprobemos.

Caso 3: p moviles

Para el caso general lo podemos plantear de manera recursiva: supongamos que tenemos pmoviles y n pisos. Lanzamos un movil desde el piso i:

• Si se rompe, el problema se reduce a p− 1 moviles y i− 1 pisos.

• Si no se rompe, el problema se reduce a p moviles y n− i pisos.

Por tanto, para cada piso i podemos calcular cuantos intentos como maximo nos llevara silanzamos el movil desde ese piso (haciendo el maximo de los casos anteriores).

Podemos definir una funcion recursiva:intentos(p,n)= 1 + min(max(intentos(p-1,i-1),intentos(p,n-i))

Los casos base seran:

• intentos (1, n) = n

• intentos de (p, 1) = 1

• intentos de (p, 0) = 0

Page 10 of 41

Page 11: Algorithmics and Programming 2

//Devuelve el numero minimo de intentos en el worst case, dados n pisos y

//p moviles

int intentos(int p,int n)

//Casos base

if(p==1) return h;

if(n==0) return 0;

if(n==1) return 1;

int minimo = n;

for(int i=1;i<=n;i++)

//aplicamos recursividad

minimo = min(minimo,(1+max(intentos(p,n-x),intentos(p-1,i-1))));

return minimo;

Se puede hacer mas eficiente si almacenamos en una matriz el resultado de la funcion para cadai < p moviles y para cada j < n pisos. De esta forma nos ahorramos muchas llamadas recursivasinnecesarias.

int solvepuzzle(int p,int n)

//Declaramos la matriz que contendra el numero mınimo de intentos en

//el peor caso para cada i moviles y j pisos

int numdrops[p+1][n+1];

int i,j,x;

//Casos base

for(i=0;i<=n;i++) numdrops[0][i]=0;

for(i=0;i<=n;i++) numdrops[1][i]=i;

for(j=0;j<=p;j++) numdrops[j][0]=0;

//Llenamos la matriz

for(i=2;i<=p;i++)

for(j=1;j<=n;j++)

int minimum=n;

for(x=1;x<=j;x++)

minimum=min(minimum,

(1+max(numdrops[i][j-x,numdrops[i-1][x-1])));

//Asignamos el valor mınimo de intentos a la posicion i,j

numdrops[i][j]=minimum;

Fuente del codigo: Brilliant.orgAuthor: Hector Sama

Page 11 of 41

Page 12: Algorithmics and Programming 2

Divide & Conquer

8. A, B or C.

Suppose you are choosing between the following three algorithms. What are the running times ofeach of these algorithms (in big-O notation), and which one would you choose?

• Algorithm A solves problems by dividing them into five subproblems of half the size, recursivelysolving each subproblem, and then combining the solutions in linear time.

Solution: Apply Master’s theorem to T (n) = 5T (n/2) + O(n), where a = 5, b = 2, c = 1.Hence the complexity is

O(nlog2 5

)= O

(n2.32

).

• Algorithm B solves problems of size n by recursively solving two subproblems of size n − 1 andthem combining the solutions in constant time.

Solution: We can solve the recurrence

T (n) = 2T (n− 1) + O(1) = 2[2T (n− 2) + O(1)] =

= 4[2T (n− 3) + O(1)] = 8T (n− 3) + 7O(1) = ... =

= 2kT (n− k) + (2k − 1)O(1).

If k = n,2nT (0) + (2n − 1)O(1) = (2n+1 − 1)O(1) = O(2n).

We can get the same result applying Muster Theorem with a = 2, b = 1, c = 0 =⇒ T (n) =2T (n− 1) + O(1) = O(2n).

• Algorithm C solves problems of size n by dividing them into nine subproblems of size n/3, recur-sively solving each subproblem, and then combining the solutions in O(n2) time.

Solution: Apply Master’s theorem to T (n) = 9T (n/3) +O(n2), where a = 9, b = 3, c = 2. Thecomplexity is

O(n2 log n).

To choose the best algorithm, we must compare A and C because algorithm B is clearly worse.Then, as

limn→∞

n2.32

n2 log n= [by l’Hopital’s Rule] = lim

n→∞

0.32n−0.68

n−1= 0.32 lim

n→∞n0.32 →∞,

we conclude that algorithm C is the best.

Author: Ramon Ventura Navarro

Page 12 of 41

Page 13: Algorithmics and Programming 2

9. The majority element.

A majority element in a vector, A, of size n is an element that appears more than n/2 times(thus, there is at most one). For example: the vector [3, 3, 4, 2, 4, 4, 2, 4, 4] has a majority element(4), whereas the vector [3, 3, 4, 2, 4, 4, 2, 2] does not. Here is a sketch of an algorithm to solve theproblem:

First, a candidate majority element is found (this is the hardest part). This candidate is the onlyelement that could possibly be the majority element. The second step determines if this candidate isactually the majority. This is just a sequential search through the vector. To find a candidate in thevector, A, form a second vector, B. Then compare A0 and A1. If they are equal, add one of these toB; otherwise do nothing. Then compare A2 and A3. Again if they are equal, add one of these to B;otherwise do nothing. Continue in this fashion until the entire vector is read. The recursively find acandidate for B; this is the candidate for A (why?).

• How does the recursion terminate?

Solution: The recursion terminates when the size of the vector is:

– 0→ the vector is empty so there is no majority element.

– 1→ the vector contains the candidate for majority element.

• What is the running time of the algorithm?

Solution: Worst case: all the elements of the vector are the same.

T (n) = T(n

2

)+ O(n).

By using the Master Theorem with a = 1, b = 2 and c = 1 we conclude that T (n) ∈ O(n).

• How can we avoid using an extra array, B?

Solution: We are looking for a procedure that works similarly but only using the originalvector A. Notice that we cannot lose information about any element because we need allof them to verify that the selected candidate is the majority element.

The core idea is to keep the discarded elements by swapping them with those stored in thetail of the vector. With two indices, i and j, we can determine the region of elements thatsurvive (A[0 . . . i − 1]), those that are discarded (A[j + 1 . . . n]) and those that have notbeen compared yet (A[i . . . j]).

At each iteration we compare the elements A[i] and A[i + 1]:

– If they are equal, we swap A[i + 1] with A[j], increase i and decrease j.

– If they are different, we swap A[i, i + 1] with A[j − 1, j] and decrease j by two.

Notice that i and j are approaching by two units at each iteration. We will have to executen/2 iterations.

At the next step of the algorithm, the same procedure will be recursively executed for thesegment A[0 . . . j].

• Prove the correctness of the algorithm (hint: prove it for n even)

• How is the case where n is odd handled?

Page 13 of 41

Page 14: Algorithmics and Programming 2

Solution: To prove the algorithm is correct (for n even), we need to prove that

a majority element of A =⇒ a majority element of B.

Suppose a is the majority element in A. Let pkk be the number of pairs of elements kk andpkl be the number of pairs of elements kl. The number of occurrences of a in the vector is2paa + pak. If a is the majority element, then

2paa + pak >n

2

and the total number of pairs in the vector A is

n

2= paa + pak + pkk + pkl.

By substituting n/2 in the previous inequality we have

paa > pkk + pkl ≥ pkk.

The size of B will be paa + pkk ≤ n2 . From the previous inequality we deduce that the

number of occurrences of a will be higher than those of any other element (paa > pkk),hence a is also the majority element of B.

In case n is even, we can proceed as follows. Pick one element from A, e.g., x and checkwhether x is the majority element by comparing it with all the remaining elements.

– If x is the majority element, we are done.

– If not, we remove x from the vector and we proceed as for the case of n being even.

Author: Laura Garcıa

Page 14 of 41

Page 15: Algorithmics and Programming 2

10. Breaking into pieces.

Let us assume that f is Θ(1) and g has a runtime proportional to the size of the vector it has toprocess, i.e., Θ(j − i + 1). What is the asymptotic cost of A and B as a function of n? (n is the sizeof the vector).

double A(vector<double>& v, int i, int j)

if (i < j)

int x = f(v, i, j);

int m = (i+j)/2;

return A(v, i, m-1) + A(v, m, j) + A(v, i+1, m) + x;

else

return v[i];

double B(vector<double>& v, int i, int j)

if (i < j)

int x = g(v, i, j);

int m1 = i + (j-i+1)/3;

int m2 = i + (j-i+1)*2/3;

return B(v, i, m1-1) + B(v, m1, m2-1) + B(v, m2, j) + x;

else

return v[i];

Solution: In both programs, we have to understand how the algorithm works and apply theMaster theorem to find out the values of a, b and c. With that information, we can discover itscomplexity.

A) As we can see, the computation of x has constant cost as it takes the value from f(v, i, j),which is constant (O(1)).

When we take m = (i + j)/2, we are dividing the vector v by half and generate three recursivecalls: A[i . . .m− 1], from A[m. . . j] and from A[i + 1 . . .m], each call having half vector.

Thus we have T (n) = 3T (n/2) + O(1), with a = 3, b = 2 and c = 0. As a > bc : 3 > 1, thisalgorithm has a complexity of O(nlog2 3).

B) The second one is quite similar to the previous one. The computation of x becomes O(j−i+1)in each iteration. If we consider the size of the vector as n = j−i+1, then the cost of g(v, i, j)

is O(n). In this case, we call B three times, but with a ratio of 1/3. So, T (n) = 3T (n/3) + O(n)with a = 3, b = 3 and c = 1. As a = bc, the cost is O(n log n).

In case we could choose A or B, it would be better B as it has a better asymptotic cost.

Author: Alexandra Gonzalez Alvarez

Page 15 of 41

Page 16: Algorithmics and Programming 2

Memory Management

11. Constructors and Destructors.What is the output of this program? Explain why.

int c = 0; // Global variable

class A

int id;

public:

A (): id (++ c) cout << "N";

A (const A & x)

id = x.id; cout << "C";

A & operator = (const A & x)

id = x.id; cout << "A";

~A () cout << id;

;

void f(const A& x, A y)

A z = x;

A w;

int main()

A v1, *v2, v3;

A v4 = v3;

v2 = new A();

v1 = *v2;

f(v1, v4);

delete v2;

A v5;

Solution: We start by exploring the behavior of the constructors, methods and destructors ofclass A.

int c = 0; // Global variable

class A

int id; //Value given to each object of class A

public:

//Constructor that creates object of class A with the value of 'id'

// (increases each time)

A() : id(++c) cout << "N";

//Constructor used when we create an object that copies an existent one (x)

A(const A& x)

id = x.id; cout << "C";

//Changes the value (id) of an existent variable with the value of another one

A& operator=(const A& x)

id = x.id; cout << "A";

//Destructor that prints the id of the object deleted

~A() cout << id;

;

Now that we created the class, we analyze each instruction that is executed in the main.

The first instruction is:

Page 16 of 41

Page 17: Algorithmics and Programming 2

A v1, *v2, v3; //Current output: NN.

which creates two objects of class A: v1 and v3, using the first constructor of the class (prints”N”). v1 will have id = 1 and v3, id = 2. v2 is a pointer created to point a memory addresscontaining an object of class A.

A v4 = v3; //Current output: NNC.

v4 is a new object of class A created as a copy of v3 (using the second constructor that prints”C”). v4 will have the same value as v3 (id = 2).

v2 = new A(); // Current output: NNCN

new A() creates an object of class A and returns a pointer to this object. v2 will start pointingthis new object (which has been created using the first constructor). The object (*v2) will haveid = 3.

v1 = *v2; // Current output: NNCNA

v1 is assigned the value of the object pointed by v2 (we use the operator of the class that prints”A”). v1’s id changes from 1 to 3 (id = 3 like *v2).

Next, the function f is called. We examine what the output of f is. We call the function f usingthe objects v1 and v4.

//We pass v1 by constant reference (does not create a copy)

//v4 is passed by value and it creates a copy, which is 'y' (y = v4 with id = 2)

// (second constructor)

void f(const A& x, A y) //Current output: NNCNAC

//z is created as a copy of x (second constructor that prints "C")

//z will have id = 3

A z = x; //Current output: NNCNACC

//An object of class A is created (first constructor that prints "N")

//w will have id = 4

A w; //Current output: NNCNACCN

//y, z and w are local variables of the function

As the function does not return or change anything, all the variables created there will be deleted.Most recent elements are the first ones to be deleted (w first, then z and finally y).

f(v1, v4); //Current output: NNCNACCN432.

After using the function, when we delete the local variables, we use the destructor (prints theid of the object).

delete v2; //Current output: NNCNACCN4323

Now, ”delete” deletes an object (using the destructor) through a pointer without erasing thepointer itself. v2 should not be used again if the memory address that it has is not changed.

A v5; //Current output: NNCNACCN4323N

v5, an object of class A, is created (first constructor). v5 will have id = 5.

Page 17 of 41

Page 18: Algorithmics and Programming 2

When the program has finished all the variables that have not been destructed yet are deleted(destructor that prints id). The objects that exist are v1 (id = 3), v3 (id = 2), v4 (id = 2), v5(id = 5) and v2 (pointer). The first one to be created is last to be deleted.Hence, the final output is: NNCNACCN4323N5223.

Author: Andrea Bellmunt Fuentes

12. List with pointers.Consider the following definition of a list of students organized as shown in the picture.

struct Student

string name;

vector<double> marks;

Student* next;

;

string BestStudent(Student* L);

You can assume that the vector of marks is never empty. The last student in the list points at“null” (nullptr).

Design the function BestStudent with the following specification:

L points at the first student of the list. BestStudent returns the name of thestudent with the best average mark. In case no student has an average markgreater than or equal to 5, the function must return the string ”Bad Teacher”.

Solution:

// Receives a vector with a student’s marks and returns the average mark

// precondition: the vector can’t be empty

double compute_average(const vector<double> &marks)

double sum = 0;

for (double mark : marks)

sum += mark; // calculate the sum of elements

return sum / marks.size(); // return the avg. value

// Receives a pointer to the first student in a linked list.

// Returns the name of the student with the highest avg. mark

string BestStudent(Student *L)

string output = "Bad Teacher";

Page 18 of 41

Page 19: Algorithmics and Programming 2

double max_average = 5;

while (L != nullptr) // if nullptr, we have reached the end of the list

double current_average = compute_average(L->marks); // cur. st. avg. mark

if (current_average >= max_average)

output = L->name;

max_average = current_average;

L = L->next; // iterate to the next student

return output;

Author: Pol Puigdemont Plana

Page 19 of 41

Page 20: Algorithmics and Programming 2

Containers: Stack

13. Middle element of a stack.Design the class MidStack implementing a stack with the following operations:

• Push/pop: the usual operations on a stack.

• FindMiddle: returns the value of the element in the middle.

• DeleteMiddle: deletes the element in the middle.

All the operations must be executed in O(1) time.

Suggestion: use some container of the STL to implement it.

Note: if the stack has n elements at locations 0 . . . n− 1, where 0 is the location at the bottom, themiddle element is the one at location b(n− 1)/2c.

Solution:

template <typename T> class MidStack

public:

// Constructor.

MidStack()

// Adds an element to the stack.

void Push(const T &elem)

q.push_back(elem);

if (v.size() < q.size())

v.push_back(q.front());

q.pop_front();

// Remove the top element of the stack.

void Pop()

assert(not v.empty());

if (q.empty())

v.pop_back();

else

q.pop_back();

if (v.size() - 1 > q.size())

q.push_front(v.back());

v.pop_back();

// Returns the value of the element in the middle.

const T &FindMiddle() const

assert(not v.empty());

return v.back();

Page 20 of 41

Page 21: Algorithmics and Programming 2

// Deletes the element in the middle.

void DeleteMiddle()

assert(not v.empty());

v.pop_back();

if (v.size() < q.size())

v.push_back(q.front());

q.pop_front();

private:

vector<T> v; // This vector stores the first half elements of the stack.

// It contains ceil(n/2) elements. The last element of this

// vector will be the middle element of the stack.

deque<T> q; // This double end queue stores the second half elements of

// the stack. It contains floor(n/2) elements.

;

Author: Sergio Cardenas Gracia

Page 21 of 41

Page 22: Algorithmics and Programming 2

Containers: Queues and Lists

14. Queues implemented as circular buffers.Design the class queue implemented with a circular buffer (using a vector):

• The push/pop/front operations should run in constant time.

• The copy and delete operations should run in linear time.

• The class should have a constructor with a parameter n that should indicate the maximumnumber of elements in the queue.

Consider the design of a variable-size queue using a circular buffer. Discuss how the implementationshould be modified.

Solution:

template <typename T>

class Queue

private:

int Size; // current size of the queue

vector<T> Objects; // vector to store the queue elements

int read; // index of the front element in the queue

int write; // index of the last element in the queue + 1

public:

// Constructor to initialize a queue with capacity n

Queue(int n) : Size(0), Objects(vector<T>(n)), read(0), write(0)

// Returns the size of the queue

int size()

return Size;

// Check if the queue is empty

bool isEmpty()

return size() == 0;

// Check if the queue is full

bool isFull()

return size() == Objects.size();

// Dequeues the front element

void pop()

assert(not isEmpty());

read = (read + 1) % Objects.size();

--Size;

// Enqueues an item

Page 22 of 41

Page 23: Algorithmics and Programming 2

void push(const T& x)

if (not isFull())

Objects[write] = x;

write = (write + 1) % Objects.size();

++Size;

else

for (int i = 0; i < write; ++i) Objects.push_back(Objects[i]);

Objects.push_back(x);

++Size;

write = 0;

// Returns the front element of the queue

T front()

assert(not isEmpty());

return Objects[read];

;

Author: Sonia Castro Paniello

15. Merge Sort.• Design the method merge(const List& L) that merges the list with another list L, assuming thatboth lists are sorted. Assume that a pair of elements can be compared with the operator <.

• Design the method sort() that sorts the list according to the < operator. Consider merge sortand quick sort as possible algorithms.

Solution:

list<int> merge(const list<int>& L1, const list<int>& L2)

list<int> result = ; //aquesta sera la llista que returnem.

//creem els dos iteradors que recorreran les llistes.

list<int>::const_iterator it1 = L1.begin(), it2 = L2.begin();

//mentre els dos iteradors no estiguin al final seguirem comparant.

while (it1 != L1.end() && it2 != L2.end())

if (*it1 <= *it2) //fiquem al final de la llista result el mes petit dels dos.

result.push_back(*it1);

++it1;

else

result.push_back(*it2);

++it2;

while (it1 != L1.end()) //aixo ens serveix per recorrer les llistes fins al final.

result.push_back(*it1);

++it1;

while (it2 != L2.end())

result.push_back(*it2);

Page 23 of 41

Page 24: Algorithmics and Programming 2

++it2;

return result;

void merge_sort(list<int>& L)

if (L.size() == 1)

return; //si la mida de la llista es 1 ja no hem de fer res.

list<int> L1, L2;

list<int>::iterator it1 = L.begin(), it2 = L.end(), mid = L.begin();

advance(mid, L.size() / 2); //ens servira perque no es passi el it1 del mig de la llista.

--it2;

while (it1 != mid) //separem la llista en dues.

L1.push_back(*it1);

L2.push_front(*it2);

++it1;

--it2;

if (it1 == mid)

if (L.size() % 2 == 1)

L1.push_back(*it1);

//truquem recursivament la funcio fins que siguin llistes d'1 element

// i es vagi juntant amb el merge.

if (L.size() != 2)

merge_sort(L2);

L = merge(L1, L2);

merge_sort(L1);

else

L = merge(L1, L2);

bool sorted(const list<int>& L) //boolea per veure si esta sorted.

list<int>::const_iterator it1 = L.begin(), it2 = L.begin();

++it2;

while (it2 != L.end())

if (*it1 > *it2)

return false;

++it1;

++it2;

return true;

void quick_sort(list<int>& L)

if (L.size() == 0) //si la llista esta buida no fem res.

return;

list<int> L1, L2;

list<int>::iterator pivot = L.begin(), pivotswap = L.end();

int aux;

advance(pivot, rand() % L.size()); //agafem un pivot aleatori dins de la llista.

--pivotswap; //fem que el pivotswap apunti cap a l'ultim element no null.

aux = *pivot;

*pivot = *pivotswap; //fiquem el pivot a la dreta del tot intercanviant-lo amb el pivotswap.

*pivotswap = aux;

for (int x : L)

if (x <= *pivotswap) //si trobem un valor major que el pivot ens ho fiquem a la llista L1.

L1.push_back(x);

else

L2.push_back(x); //si en canvi es mes petit ho fiquem a la L2.

Page 24 of 41

Page 25: Algorithmics and Programming 2

//no podrem fer el merge si la mida d'alguna de les llistes es mes d'1.

if (L1.size() != 1 or L1.size() == 0)

quick_sort(L1);

if (L2.size() != 1 or L1.size() == 0)

quick_sort(L2);

L = merge(L1, L2);

Author: Rodrigo Bonferroni

Page 25 of 41

Page 26: Algorithmics and Programming 2

Graphs: Connectivity

16. DFS Stack Overflow.• DFS can be implemented with an elegant recursive algorithm, but it may experiment stack overflowproblems. Explain why.• Design an iterative version of DFS.Challenge: do not to search in...

Solution: When a function is called in C or C++, variables are created and copied into amemory segment called stack. This memory has limited capacity, and if you make too manyrecursion calls (this happens when the graph where you make DFS is too large), a stack overflowoccurs; that is, you exceed the memory space dedicated to the stack. Iterative DFS versions arepreferable in these cases. Here we present a possible implementation using Python.

def DFS(G, s):

visited = [False for i in range(G.V)] # all vertices

stack = []

stack.append(s)

added_to_stack = [False for i in range(G.V)]

added_to_stack[s] = True

while (len(stack)):

s = stack[-1]

stack.pop()

if (not visited[s]):

print(s, end=' ')

visited[s] = True

# auxiliar function that returns all nodes adjacent to s

for node in G.adj[s]:

if (not added_to_stack[node]):

stack.append(node)

added_to_stack[node]= True

Author: Martı Farre Farrus

Page 26 of 41

Page 27: Algorithmics and Programming 2

17. DFS (from DPV2008).Perform DFS on the two graphs. Whenever there is a choice of vertices, pick the one that isalphabetically first. Classify each edge as a tree edge, forward edge, back edge or cross edge, andgive the pre and post number of each vertex.

Solution:

• Tree edges: those in the DFS forest.

• Forward edges: lead to a nonchild descendant in the DFS tree.

• Back edges: lead to an ancestor in the DFS tree.

• Cross edges: lead to neither descendant nor ancestor.

Legend of the different classes of edges.

Page 27 of 41

Page 28: Algorithmics and Programming 2

To the right, the graph with the pre and post numbers. To the left, the DFS tree.

First Graph

Secong Graph

Author: Claudia Mur Planchart

18. Streets in Computopia.The police department in the city of Computopia has made all streets one-way. The mayor contendsthat there is still a way to drive legally from any intersection in the city to any other intersection,but the opposition is not convinced. A computer program is needed to determine whether the mayoris right. However the city elections are coming up soon, and there is just enough time to run alinear-time algorithm.

• Formulate this problem graph-theoretically, and explain why it can indeed be solved in lineartime.

Solution: Aquest problema es pot representar mitjancant un graf dirigit G = (V,E) oncada interseccio es un vertex v i hi ha una aresta entre interseccions (u, v) si hi ha unacarretera que va directament d’u a v. L’afirmacio de que hi ha una manera de conduirlegalment des de qualsevol interseccio fins a qualsevol altra interseccio es cert si, i nomessi, hi ha un camı des de cada vertex de G fins a tots els altres vertexs. Aixo es cert si, inomes si, el graf esta fortament connectat, de manera que es pot resoldre en temps linealamb DFS determinant si el graf es un component fortament connectat.

• Suppose it now turns out that the mayor’s original claim is false. She next claims somethingweaker: if you start driving from town hall, navigating one-way streets, then no matter whereyou reach, there is always a way to drive legally back to the town hall. Formulate this weakerproperty as a graph-theoretic problem, and carefully show how it too can be checked in lineartime.

Solution: Utilitzem el mateix graf G del primer apartat i establim l’ajuntament en unvertex t. Aquesta propietat es certa si, i nomes si, no hi ha cap camı des de t a qualsevol

Page 28 of 41

Page 29: Algorithmics and Programming 2

vertex v del graf de manera que no hi ha camı de tornada de v a t. Es a dir, que si hi haun camı de t a v, obligatoriament ha d’haver-hi un de tornada de v a t. En altres paraules,no pot haver un camı des de t a un vertex fora de la seva component connectada.

Per determinar si el graf te aquesta propietat, s’haurien de buscar les components connec-tades i llavors fer una DFS des de t. Si des de t es pot arribar a un vertex que esta en unacomponent connectada diferent, llavors la nova afirmacio es falsa.

Author: Adrian Cerezuela Hernandez

19. Pouring Water (from [DPV2008]).We have three containers whose sizes are 10 pints, 7 pints and 4 pints, respectively. The 7-pint and4-pint containers start out full of water, but the 10-pint container is initially empty. We are allowedone type of operation: pouring the contents of one container into another, stopping only when thesource container is empty or the destination container is full. We want to know if there is a sequenceof pourings that leaves exactly 2 pints in the 4-pint container.

• Model this as a graph problem: give a precise definition of the graph involved and state thespecific question about this graph that needs to be answered.

• What algorithm should be applied to solve the problem?

• Give a sequence of pourings, if it exists, or prove that it does not exist any sequence.

Hint: A vertex of the graph can be represented by a triple of integers.

Solution:En el problema Pouring Water disposem de tres contenidors (10 pintes, 7 pintes i 4 pintes) Elsdos mes petits comencen inicialment plens i el mes gran buit i el que hem d’aconseguir es tenirnomes 2 pintes en el petit. Per fer-ho podem abocar aigua d’un contenidor a un altre, la maximapossible fins que el receptor estigui ple o l’emissor buit.

Si modelem el problema en forma de graf, podem emmagatzemar la quantitat d’aigua que hi ha acada contenidor en tuples de tres elements. Aquesta informacio es guardara a cada node del graf,i per tant, el nostre objectiu a assolir es una tupla del tipus (x, y, 2). Ates que a un ordinador noli podem dir que vagi traspassant aigua d’un lloc a un altre fins que contingui la quantitat desit-jada, el que fem es generar les possibles combinacions de manera recursiva. A l’aplicar aquestaestrategia, el que aconseguirem es un graf connex que contindra totes les possibles combinacions,i un altre, encara que no el tinguem implıcitament, d’aquelles combinacions que no son possibles.

La metodologia emprada per a la resolucio d’aquest exercici es l’aplicacio de l’algoritme DFS. Sibe es cert, en aquest cas es podria implementar utilitzant tambe un algoritme BFS i d’aquestamanera aconseguirıem el camı mes curt de pas. Aixı doncs, partint del node inicial (0, 7, 4),generarem tots els possibles vertex i observarem que sı que existeix un seguit d’abocaments quepermeten obtenir 2 pintes en el contenidor mes petit.

Aquest problema ha estat resolt mitjancant un vector de vectors de vectors simulant una matriutridimensional per tal de no repetir combinacions durant la generacio. De totes maneres, si ensvolguessim estalviar l’espai de memoria que aquesta matriu pot arribar a ocupar, el que podrıemfer es emmagatzemar les dades en un conjunt per tal d’evitar sobre costos. El motiu pel qual nos’ha fet aixı es perque treballar amb un conjunt d’estructures es mes complex i tampoc ho creiaconvenient per la resolucio del problema.

Page 29 of 41

Page 30: Algorithmics and Programming 2

Finalment, podem observar l’arbre resultant d’aplicar un BFS i la sequencia mes curta perarribar al destı.

Author: Pol Lizaran Campano

Page 30 of 41

Page 31: Algorithmics and Programming 2

Containers: Priority Queue

20. The K-th Element.The K-th element of n sorted vectors.Let us consider n vectors sorted in ascending order. Design an algorithm with cost Θ(k log n + n)that finds the K-th global smallest element.

Solution: Proposem un algorisme en pseudocodi.

// Farem servir una estructura com la seguent:

struct Element

int valor;

int index;

int posicio;

;

// valor = valor de l’element que afegirem a la cua de prioritats

// index = numero (o ındex) del vector al qual pertany

// posicio = la posicio a la que es troba dins de tal vector

funcio buscar_k (...)

// k iteracions per trobar el k-essim petit

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

minim_pq = pq.top() //Mınim de la cua de prioritats

pq.pop()

++minim.posicio

// si encara tenim candidats del vector (minim.index) per posar-ho

// dins la cua de prioritats

If (minim.posicio < v[minim.index].size())

minim.element = v[minim.index][minim.posicio]

Posem el nou element a la cua de prioritats

return minim.valor

int main()

llegir n vectors (vn) i afegir-los en un altre vector (v);

afegir el primer element de cada vector (vn) a la priority_queue<Element>pq;

// ordenarem la cua de prioritats segons el valor

cout << buscar_k (pq, v) << endl;

Analisi del temps

• Ordenar la cua (tindra com a maxim 1 element/vector, es a dir un total de n elementsdins la cua) es O log n (logarıtimic). Ordenarem la cua k vegades (traurem k vegades, unelement cada vegada, de la cua) k =⇒ logn.

• A la funcio main, quan inicialitzem la cua i posem els elements =⇒ n (lineal).

Temps total: Θ(k log n + n)Author: Wenli Pan

Page 31 of 41

Page 32: Algorithmics and Programming 2

Graphs: Shortest Path

21. New road (from [DPV2008]).There is a network of roads G = (V,E) connecting a set of cities V . Each road in E has an associatedlength le.There is a proposal to add one new road to this network, and there is a list E′ of pairs of citiesbetween which the new road can be built. Each such potential road e′ ∈ E′ has an associated length.As a designer for the public works department you are asked to determine the road e′ ∈ E′ whoseaddition to the existing network G would result in the maximum decrease in the driving distancebetween two fixed cities s and t in the network.Give an efficient algorithm for solving this problem.

Solution: Tenim: G = (V,E), Cada e ∈ E te assignada una longitud, Una llista E′, cadae′ ∈ E′ te assignada una longitud. Objectiu: Minimitzar el camı mes curt entre s, t ∈ V fixatsafegint una aresta e′ ∈ E′.

Per a saber si un camı es mes curt o no al afegir un vertex (u, v) cal veure si es compleix ladesigualtat triangular: d(v) < d(u) + w(u, v). Com que el graf te pesos, no hi ha una distanciamınima ni una distancia maxima, per tant cal revisar totes les arestes de la llista E′. Volemminimitzar el camı entre s i t afegint una aresta (u, v). Llavors, si considerem s com la font id(u), d(t) com les distancies entre s i u, t respectivament, d′(v) com la distancia entre t i v iw(u, v) com la distancia entre u i v, hem de revisar la seguent desigualtat:

d(t) < d(u) + w(u, v) + d′(v)

1) Al afegir una aresta si aquesta reduiex la distancia mınima entre s i t, el nou camı mescurt ha de passar forcosament per aquesta aresta.

2) En cas de que la aresta contingui una de les ciutats t o s, les distancies d(u) o d′(v)serien 0.

Volem revisar aquesta desigualtat per a cada aresta(u, v) ≡ e′ ∈ E′. Comencarem creant unamatriu de dues files amb |V | columnes. A la primera fila guardarem les distancies entre s i totsels vertexs de V , a la segona fila guardarem les distancies entre t i tots els vertexs de V , pertant cal fer Dijkstra dos cops (no pot haver camins negatius). Ara nomes cal fer un bucle quefaci una repeticio per cada aresta de E′ revisant la desigualtat.

for all e' ≡ (u,v) ∈ E':

if d(t) > d(u) + w(u,v) + d'(v):

d(t) = d(u) + w(u,v) + d'(v)

selected_edge = w(u,v)

I guardar l’aresta en selected edge. Aixo es: O((|V | + |E|) log |V |) + O((|V | + |E|) log |V |) +O(|E′|) = O((|V |+ |E|) log |V |).

Author: Marcel Tomas Bernal

Page 32 of 41

Page 33: Algorithmics and Programming 2

Graphs: Minimum Spanning Trees and Maximum flows

22. k-clustring of maximum spacing.We want to classify a set of points into k clusters. We define the distance between two points as theEuclidean distance. We define the spacing of the clustering as the minimum distance between anypair of points in different clusters.

Describe an algorithm such that, given an integer k, finds a k-clustering such that spacing is maxi-mized. Argue about the complexity of the algorithm.

Note: k-clustering of maximum spacing is the basis for the construction of dendograms.

Solution: The definition we give for spacing is equivalent to the method of single linkage togenerate a dendogram. A dendogram is a tree that is created by joining clusters given somedistance measure to compare pairs.

In this case, we are joining points (we will call them vertices) by taking first the ones that arecloser together. We define this associated distance to be the ”weight” of the edge between thetwo vertices. Now, we have a complete graph and we will be selecting edges (which is equivalentto joining points) in increasing order of weight until we connect all the vertices, skipping edgesthat generate cycles.

The procedure we have described is exactly Kruskal’s algorithm to generate a Minimum SpanningTree. This way, we have deduced that a dendogram that maximizes spacing is in fact a MST.Hence, we can solve the problem by applying Kruskal’s Algorithm, stopping at (n − k) joins.The intuition behind this number is that we start with n clusters (n separated vertices), and atevery join we reduce the number of clusters by one.

The complexity of the described algorithm is the same as of Kruskal’s algorithm, which isO(|E| log |V |).

Page 33 of 41

Page 34: Algorithmics and Programming 2

23. Flow network (from [DVP2008]).

• Find the maximum flow from S to T. Give a sequence of augmenting paths that lead to themaximum flow.

• Draw the residual graph after finding the maximum flow.

• Find a minimum cut between S and T.

Solution: We now give three visualizations of the requested steps.

1. Sequence of augmenting paths that lead to the maximum flow. Each color represents adifferent augmenting path. The total flow is 19.

2. Residual graph. Backward edges of the residual network are depicted in green, while remainingforward edges are shown in red.

Page 34 of 41

Page 35: Algorithmics and Programming 2

3. Min-cut of the given flow network. Its value (19) is also the value of the maximum flow.

Author: Claudia Len Manero

24. Contagious Disease.The island of Sodor is home to a large number of towns and villages, connected by an extensive railnetwork. Recently, several cases of a deadly contagious disease (Covid 19) have been reported in thevillage of Ffarquhar. The controller of the Sodor railway plans to close down certain railway stationsto prevent the disease from spreading to Tidmouth, his home town. No trains can pass through aclosed station. To minimize expense (and public notice), he wants to close down as few stationsas possible. However, he cannot close the Ffarquhar station, because that would expose him tothe disease, and he cannot close the Tidmouth station, because then he couldn’t visit his favorite pub.

Describe and analyze an algorithm to find the minimum number of stations that must be closedto block all rail travel from Ffarquhar to Tidmouth. The Sodor rail network is representedby an undirected graph, with a vertex for each station and an edge for each rail connection be-tween two stations. Two special vertices F and T represent the stations in Ffarquhar and Tidmouth.

For example, given the following input graph, your algorithm should return the number 2.

Source: Jeff Erickson, Algorithms, UIUC, 2015.

Solution:

Page 35 of 41

Page 36: Algorithmics and Programming 2

Choosing an algorithm

• Our goal is to close the minimum number of stations to protect Tidmouth.

• Therefore, we need to isolate Ffarquhar along with some stations (from the point of viewof Tidmouth).

• This process has a resemblance to the min-cut from the Max-flow problem, since it com-putes the minor capacity of the cut that would disconnect the Source Node from the TargetNode.

• . Moreover, we know that said capacity is equal to the maximum flow from S to T . Hence,it is possible to calculate it with the Ford-Fulkerson Algorithm.

Adapting the graph

1. The Ford-Fulkerson algorithm is for directed graphs. Hence, we transform each undirectededge into to directed edges: It is possible to come and go from a station to another.

2. We are interested in the number of stations to close, so we should set the capacity of everyedge to 1.

3. For each vertex u other than F and T , replace u with two new vertices u1 and u2 and addan edge (u1, u2). Connect all edges entering u to u1, and all edges exiting u, to u2.

4. F is the source and T is the sink. We now have a flow network.

Analysis

• Relation between original graph and modified graph:

– Establishing an unique edge of Ce = 1 ”inside” each modified station reflects theaction of closing or not closing the original one.

– Therefore, the Min-Cut (Max Flow) of the modified graph is equal to the number ofstations that are to be closed in the original graph.

Page 36 of 41

Page 37: Algorithmics and Programming 2

– The min-cut will always be drawn through interior egdes, because the exterior onesare extremely large. That way the obtained result will surely be the concrete numberof stations to close.

• Complexity: The graph modifications are linear and the Ford-Fulkerson Algorithm has anasymptotic cost O(|V | · |E|2).

Author: Joel Sasot Ruiz

Page 37 of 41

Page 38: Algorithmics and Programming 2

Trees

25. Drawing binary trees. We want to draw the skeleton of a binary tree as it is shown in the figure.For that, we need to assign (x, y) coordinates to each tree node. The layout must fit in a predefinedbounding box of size WxH, with the origin located in the top-left corner.Design the function

void draw(Tree T, double W, double H)

to assign values to the attributes x and y of all nodes of the tree in such a way that the lines thatconnect the nodes do not cross.Suggestion: calculate the coordinates in two steps. First assign (x, y) coordinates using some arbi-trary unit. Next, shift/scale the coordinates to exactly fit in the bounding box.

Solution:

// Node of the tree

struct TreeNode

int element;

double x;

double y;

TreeNode* left;

TreeNode* right;

;

using Tree = TreeNode*;

// Bounding box of the tree before scaling (ymin is assumed to be zero)

struct BoundingBox

double xmin;

double xmax;

double ymax;

;

// Assigns coordinates to the nodes of T. The coordinate (x,y) is

// assigned to the root, while the coordinates (x-scale, y+1)

// and (x+scale, y+1) is assigned to the root of the left

// and right subtrees. The separation between nodes of the same level

// is halved at each level.

void coord(Tree T, double x, double y, double scale, BoundingBox& BB)

if (T == nullptr)

return;

T->x = x;

T->y = y;

BB.xmin = min(BB.xmin, x);

BB.xmax = max(BB.xmax, x);

BB.ymax = max(BB.ymax, y);

coord(T->left, x - scale, y + 1, scale / 2, BB);

coord(T->right, x + scale, y + 1, scale / 2, BB);

// The coordinates of T are shifted "-shift" units and scaled

// by the xscale and yscale factors.

Page 38 of 41

Page 39: Algorithmics and Programming 2

void scaleTree(Tree T, double shift, double xscale, double yscale)

if (T == nullptr)

return;

T->x = (T->x - shift) * xscale;

T->y *= yscale;

scaleTree(T->left, shift, xscale, yscale);

scaleTree(T->right, shift, xscale, yscale);

// Defines the coordinates of the nodes of T in such a way

// that the tree exactly fits in the bounding box (0,0)-(W,H).

// The tree is drawn in such a way that the separation between

// nodes of the same level is halved at every level.

void draw(Tree T, double W, double H)

BoundingBox BB = 1, 1, 0 ;

coord(T, 1, 0, 0.5, BB);

double width = BB.xmax - BB.xmin;

// Special case: width = 0 ==> only one node

// We just assign coordinate (0,0)

if (width == 0)

T->x = T->y = 0;

return;

double shift = BB.xmin;

double xscale = W / width;

double yscale = H / BB.ymax;

scaleTree(T, shift, xscale, yscale);

void print(Tree T)

if (not T)

return;

cout << T->element << " (" << T->x << "," << T->y << ")" << endl;

print(T->left);

print(T->right);

// Frees the tree

void free(Tree T)

if (not T)

return;

free(T->left);

free(T->right);

delete T;

// An example

int main()

Tree n1 = new TreeNode 1, 0, 0, nullptr, nullptr ;

Page 39 of 41

Page 40: Algorithmics and Programming 2

Tree n2 = new TreeNode 2, 0, 0, nullptr, nullptr ;

Tree n3 = new TreeNode 3, 0, 0, nullptr, nullptr ;

Tree n4 = new TreeNode 4, 0, 0, nullptr, nullptr ;

Tree n5 = new TreeNode 5, 0, 0, nullptr, nullptr ;

Tree n6 = new TreeNode 6, 0, 0, nullptr, nullptr ;

Tree n7 = new TreeNode 7, 0, 0, nullptr, nullptr ;

n1->left = n2;

n1->right = n3;

n2->left = n4;

n2->right = n5;

n5->left = n7;

n3->left = n6;

draw(n1, 4, 8);

print(n1);

free(n1);

Page 40 of 41

Page 41: Algorithmics and Programming 2

FFT

26. Multiplication.Consider the polynomials 1 + x− 2x2 + x3 and −1 + x2.

• Choose an appropriate power of two to execute the FFT for the polynomial multiplication.Find the value of ω.

• Give the result of the FFT for −1 + x2 using the value of ω required for the multiplication (noneed to execute the FFT).

Solution: The appropiate power of two to execute the FFT must be n ≥ d+1 where d = dA+dB .A(x) = (1, 1,−2, 1) so dA = 3 and B(x) = (−1, 0, 1) so dB = 2.d = 3 + 2 = 5.n ≥ d + 1 = 5 and n must be a power of 2 ⇒ n = 8Knowing that ω is the first of the n-root of unity and we have n = 8:

ω =√i

To give the result of the FFT for B(x) using the value of ω required for the multiplication weonly have to evaluate B(x) in the eight 8-roots of unity:

B(w0) = B(1) = 12 − 1 = 0

B(w1) = B(√i) =

√i2− 1 = −1 + i

B(w2) = B(i) = i2 − 1 = −2

B(w3) = B(−√−i) = (−

√−i)2 − 1 = −1− i

B(w4) = B(−1) = (−1)2 − 1 = 0

B(w5) = B(−√i) = (−

√i)2 − 1 = −1 + i

B(w6) = B(−i) = (−i)2 − 1 = −2

B(w7) = B(√−i) =

√−i2 − 1 = −1− i

FFT (B(x), ω =√i) =

(0,−1 + i,−2,−1− i, 0,−1 + i,−2,−1− i

)Author: Martı Pons Mir

Page 41 of 41


Recommended