Dati e Algoritmi I (Pietracaprina)
II Compitino/Scritto Completo 30/01/2017
(Seconda Parte)
SOLUZIONI
Dati e Algoritmi I (Pietracaprina): I appello 30/01/2017
1
Problema. Sia S una stringa di n bit. Si definisce sandwich una sottostringa S[i . . . j], con
0 ≤ i < j < n, tale che: S[i] = 0, S[j] = 0, e S[`] = 1 per ogni i < ` < j. Assumendo S[0] = 0,
il seguente algoritmo determina inizio e lunghezza della più lunga sottostringa sandwich in S.
init ← null; length ← 0; last ← 0;
for i ← 1 to n-1 do {
if (S[i]=0) then {
if (i-last+1 > length) then {init ← last; length ← i-last+1}
last ← i
}
}
return (init,length)
a. Trovare un invariante per il ciclo for che specifichi cosa rappresentano le variabili init,
length e last alla fine di ogni iterazione.
b. Dimostrare che se l’invariante vale alla fine di un’iterazione i, allora vale alla fine
dell’iterazione i + 1 (per i ≥ 1 e i + 1 < n).
Soluzione.
a. L’invariante è il seguente:
• La più lunga sottostringa sandwich nel prefisso S[0 . . . i] ha lunghezza “length” e,
se tale lunghezza è > 0, inizia all’indice “init”.
• Il bit a 0 di indice massimo in S[0 . . . i] è S[last].
b. Si supponga che l’invariante valga alla fine dell’iterazione i e si consideri l’iterazione
i + 1. Distinguiamo due casi a seconda del valore di S[i + 1]. Se S[i + 1] = 1 non
viene eseguita alcuna operazione. Dato che in questo caso la più lunga sottostringa
sandwich nel prefisso S[0 . . . i] è anche la più lunga sottostringa sandwich nel prefisso
S[0 . . . i+1], e il bit a 0 di indice massimo in S[0 . . . i] è anche il bit a 0 di indice massimo
in S[0 . . . i + 1], l’invariante rimane banalmente vero. Se invece S[i + 1] = 0, il bit a 0 di
indice massimo in S[0 . . . i + 1] è chiaramente S[i + 1], mentre la più lunga sottostringa
sandwich nel prefisso S[0 . . . i + 1] può essere o la più lunga sottostringa sandwich nel
prefisso S[0 . . . i], oppure la sottostringa sandwich che termina in S[i + 1]. Da queste
osservazioni, e dal fatto che l’invariante valeva alla fine dell’iterazione i, si deduce che
le operazioni svolte dall’algoritmo nella iterazione i + 1 mantengono vero l’invariante
anche alla fine di tale iterazione
2
Problema. Sia T un albero binario di ricerca di altezza h che memorizza n entry che
rappresentano piloti di aerei. Ogni entry ha come chiave il numero di ore di volo del pilota e
come valore un flag binario che vale 1 se il pilota ha il grado di capitano e 0 altrimenti. Ogni
nodo v ∈ T memorizza un valore v.cap che dice quanti capitani esistono in Tv (sottoalbero
con radice v), cioè quante delle entry in Tv hanno il flag a 1. Progettare un algoritmo che dato
un intero k determini il numero di capitani con un numero di ore di volo ≤ k, e analizzarne
la complessità.
Dati e Algoritmi I (Pietracaprina): I appello 30/01/2017
2
Soluzione. Scriviamo un algoritmo ricorsivo CountCaptain(T,v,k) che dato un nodo v ∈ T
risolve il problema nel sottoalbero Tv . Per risolvere il problema in tutto l’albero T basterà
invocare l’algoritmo con v = T.root(). Lo pseudocodice è il seguente:
Algoritmo CountCaptain(T,v,k)
input Albero binario di ricerca T, nodo v ∈ T, chiave k
output numero di capitani in Tv con ≤ k ore di volo
if (T.isExternal(v)) then return 0
if (v.element().getKey() ≤ k) then
if (v.element().getValue()=1) then x ← 1 else x ← 0
return x+T.left(v).cap+CountCaptain(T,T.right(),k)
else return CountCaptain(T,T.left(),k)
Per
quanto
riguarda
la
complessità,
si
consideri
l’esecuzione
di
CountCaptain(T,T.root(),k). I nodi su cui l’algoritmo di volta in volta viene chiamato
costituiscono un percorso dalla radice a una foglia, e in ciascuna chiamata si eseguiranno
O (1) operazioni, esclusa l’eventuale chiamata ricorsiva fatta al suo interno. La complessità
sarà quindi O(h), dove h è l’altezza dell’albero.
2
Problema. Si consideri un grafo G = (V, E) con n vertici, che rappresentano città, e m
archi, che rappresentano collegamenti diretti tra città. Per ogni città i ∈ V , si ha un variabile
LV [i].place che vale MONTI se i è in montagna, MARE se i è sul mare, e NULL negli altri
casi. Progettare un algoritmo che in tempo O (n + m) determini, se esiste, una città i da cui
sono raggiungibili (con percorsi che possono attraversare diverse città) lo stesso numero di
città in montagna e di città sul mare.
Suggerimento: utilizzare come subroutine un algoritmo visto a lezione con opportune modifiche che possono essere descritte solo a parole.
Soluzione. Rappresentiamo i vertici di G con gli interi da 1 a n. L’idea è quella di determinare per ciascuna componente connessa, tramite BFS modificata, se contiene lo stesso
numero di città in montagna e di città sul mare. Appena se ne trova una si restituisce un
vertice arbitrario di essa. Siano c-monti e c-mare due variabili globali. Per ogni i ∈ V
usiamo un campo binario LV [i].visited. Si supponga di modificare l’algoritmo BFS(G,i)
in modo che quando visita un vertice j, imposti a 1 il campo LV [j].visited, e incrementi
di uno il valore di c-monti, se j è una città in montagna, oppure incrementi di uno il valore
di c-mare se j è una città sul mare. L’algoritmo richiesto è il seguente.
Dati e Algoritmi I (Pietracaprina): I appello 30/01/2017
3
Algoritmo Mari-e-Monti(G)
input Grafo G=(V,E) rappresentato con liste di adiacenza
output vertice i che raggiunge lo stesso numero di citta’ in montagna
e al mare, se esiste, 0 altrimenti
for i ←− 1 to n do LV [i].visited ←− 0
for i ←− 1 to n do {
if (LV [i].visited = 0) then {
c-monti ←− 0;
c-mare ←− 0;
BFS(G,i);
if (c-monti=c-mare) then return i
}
}
return 0
Supponiamo che G abbia c ≥ 1 componenti connesse, e siaP
mj il numero di archi della
j-esima componente connessa, per 1 ≤ j ≤ c. Si noti che m = cj=1 mj . Il costo aggregato
P
c
m
di tutte le invocazioni di BFS è O
= O (m), mentre il costo delle altre operazioni
j
j=1
fatte dall’algoritmo è O (n). La complessità finale risulta quindi essere O (n + m). Si osservi
che l’algoritmo potrebbe restituire una città i da cui si raggiungono 0 città al mare e 0 città
in montagna. Se si vuole evitare questo caso restituendo una città solo se da essa si raggiunge
lo stesso numero maggiore di 0 di città al mare e in montagna, basta aggiungere con un AND
la condizione (c-monti > 0) alla condizione (c-monti=c-mare) che compare nell’ultimo if.
2