Corso di Algoritmi e Strutture Dati—Informatica per il Management

Nome e Cognome ______________________________________ Matricola __________________
Corso di Algoritmi e Strutture Dati—Informatica per il Management
Prova Scritta, 12/1/2017
•
•
•
•
•
•
•
Chi deve recuperare il progetto del modulo 1 ha 1 ora e 30 minuti per svolgere gli esercizi 1, 2, 3
Chi deve recuperare il progetto dei moduli 2/3 ha 1 ora e 30 minuti per svolgere gli esercizi 4, 5, 6
Chi deve sostenere la prova completa ha 2 ore e 30 minuti per svolgere tutti gli esercizi proposti.
Durante la prova è consentito consultare libri e appunti.
Non è consentito l'uso di dispositivi elettronici (ad esempio, cellulari, tablet, calcolatrici elettroniche...), né
interagire in alcun modo con gli altri studenti pena l'esclusione dalla prova, che potrà avvenire anche dopo il
termine della stessa.
Le risposte devono essere scritte a penna su questi fogli, in modo leggibile e adeguatamente motivate. Le
parti scritte a matita verranno ignorate. Eventuali altri fogli possono essere utilizzati per la brutta copia ma
non devono essere consegnati e non verranno valutati.
I voti saranno pubblicati su AlmaEsami e ne verrà data comunicazione all'indirizzo mail di Ateneo
(@studio.unibo.it). Lo studente ha 7 giorni di tempo per rifiutare il voto; tutti i voti non esplicitamente
rifiutati entro tale data sono considerati accettati, e verranno in seguito verbalizzati.
BARRARE LA CASELLA CORRISPONDENTE ALLA PARTE SVOLTA
□ Svolgo solo la parte relativa al modulo 1 (esercizi 1, 2, 3);
□ Svolgo solo la parte relativa ai moduli 2/3 (esercizi 4, 5, 6);
□ Svolgo tutto il compito
NON SCRIVERE NELLA TABELLA SOTTOSTANTE
Es. 1
/5
Es. 2
/4
Es. 3
/2
/5
Es. 4
/4
Es. 5
/2
/4
Es. 6
/2
/4
Nome e Cognome ______________________________________ Matricola __________________
Esercizio 1. La curva di Koch è una figura frattale che può essere disegnata in modo ricorsivo: si
parte da un segmento S che viene diviso in tre parti uguali S1, S2, S3; si rimuove S2, e lo si rimpiazza
con due nuovi segmenti T1, T2 della stessa lunghezza di quello rimosso e disposti come i lati di un
triangolo equilatero avente come base S 2 (si faccia riferimento alla figura sotto). A questo punto si
applica nuovamente il procedimento su S1, T1, T2 e S3. L'algoritmo può essere rappresentato con lo
pseudocodice seguente:
KOCH( segmento S, int n )
if (n == 0) then
draw(S);
else
dividi S in tre parti uguali S1, S2, S3
siano T1, T2 i lati di un triangolo equilatero con base S 2
KOCH(S1, n – 1);
KOCH(T1, n – 1);
KOCH(T2, n – 1);
KOCH(S3, n – 1);
endif
// (*)
// (*)
// (*)
La figura mostra alcuni esempi di curva di Koch che si possono ottenere con vari valori di n; si nota
come a valori elevati corrisponda una curva più dettagliata.
n=0
T1
S1
T2
S3
n=1
n=2
n=3
Assumendo che le righe contrassegnate con (*) richiedano tempo di esecuzione O(1), determinare il
costo asintotico dell'algoritmo KOCH(S, n) in funzione di n, motivando la risposta.
[punti 5]
Soluzione. L'algoritmo ha costo Θ(4n); per rendersi conto di ciò è possibile contare i nodi
dell'albero di ricorsione, oppure “srotolare” l'equazione di ricorrenza T(n) = 4 T(n - 1) + c (tale
equazione di ricorrenza non può essere risolta col Master Theorem, perché non ha la struttura
richiesta per la sua applicazione)
In questo esercizio sono state indicate le equazioni di ricorrenza più fantasiose, come ad esempio
T(n) = 4 T(n/4), T(n) = 4 T((n - 1)/4), eccetera.
Nome e Cognome ______________________________________ Matricola __________________
Esercizio 2. Una rete di distribuzione dell'energia elettrica ha la struttura di un albero binario. Alla
radice dell'albero si trova la centrale elettrica, mentre ciascuna foglia rappresenta un punto di
consumo (ad esempio, uno stabilimento industriale). Ad ogni nodo v è associato un valore reale
v.val; inizialmente, a ciascuna foglia è associata l'energia consumata (in Kw), mentre i valori di tutti
gli altri nodi sono indefiniti.
Scrivere un algoritmo ricorsivo che, dato in input un riferimento alla radice T dell'albero, memorizzi
in ciascun nodo n diverso da una foglia la somma dei valori presenti in tutte le foglie del sottoalbero
avente n come radice. L'albero T è garantito essere non vuoto Ad esempio, considerando l'albero a
sinistra in cui le foglie sono etichettate con i consumi indicati, l'algoritmo deve modificare i valori
negli altri nodi come riportato a destra. Notare che il valore presente nella radice (16, in questo
caso) indica l'energia che la centrale elettrica deve fornire per poter soddisfare tutte le richieste.
[punti 4]
Centrale
elettrica
16
8
8
5
3
4
1
2
6
4
8
3
1
2
6
Punti di consumo
Determinare il costo asintotico dell'algoritmo proposto, motivando la risposta.
[punti 2]
Soluzione. Si può applicare un algoritmo di post-visita modificato. L'algoritmo seguente setta il
valore contenuto nel nodo v pari alla somme dei valori presenti nei figli (che vengono
ricorsivamente calcolati sul valore dei loro figli, fino alle foglie).
CONSUMOELETTRICO( Tree v )
if (v = null) then
return;
else
if ( v.left = null and v.right = null ) then
return; // è una foglia, non modifichiamo niente
else
CONSUMOELETTRICO(v.left);
CONSUMOELETTRICO(v.right);
v.val ← 0;
if ( v.left ≠ null ) then
v.val ← v.val + v.left.val;
endif
if ( v.right ≠ null ) then
v.val ← v.val + v.right.val;
endif
endif
endif
Nome e Cognome ______________________________________ Matricola __________________
Si presti attenzione al fatto che l'esercizio chiedeva di modificare il contenuto dei nodi non foglia
con i valori corretti. Alcune soluzioni presentate erano “quasi” corrette, nel senso che calcolavano
i valori ma non li memorizzavano nei nodi come richiesto.
Nome e Cognome ______________________________________ Matricola __________________
Esercizio 3. Si consideri un min-heap binario contenente valori interi, inizialmente vuoto.
Disegnare la struttura dell'albero che rappresenta il min-heap dopo ciascuna delle operazioni
seguenti (ogni operazione viene eseguita sullo heap che risulta al termine di quella precedente):
1. Inserimento 15
2. Inserimento 7
3. Inserimento 10
4. Inserimento 2
5. Inserimento 1
6. Cancellazione del minimo
7. Inserimento 5
8. Inserimento 3
9. Cancellazione del minimo
[Punti 5]
In questo esercizio è fondamentale ricordarsi che uno heap gode di due proprietà: 1) proprietà di
struttura, e 2) proprietà di contenuto. La proprietà di struttura richiede che lo heap sia
rappresentato da un albero completo fino al penultimo livello, e tutte le foglie dell'ultimo livello
siano “accatastate” a sinistra. Questo significa che nessuno degli alberi seguenti è uno heap,
perché questa proprietà viene violata (i primi due non sono completi fino al penultimo livello; il
terzo non ha le foglie “accatastate” a sinistra):
Oltre a questo, occorre riportare il contenuto dei nodi in modo consistente: spesso ho visto che il
contenuto dei figli sinistro e destro veniva scambiato arbitrariamente, il che non è corretto.
Nome e Cognome ______________________________________ Matricola __________________
Esercizio 4. La cabina di una funivia ha una portata massima di 1000 kg ed è in grado di contenere
al massimo 10 persone per volta. Alla partenza sono presenti n > 0 persone aventi pesi
rispettivamente p[1], p[2], … p[n]. Le persone vengono fatte salire una alla volta, iniziando da
quella di peso p[1], poi p[2] e così via, riempiendo la cabina il più possibile rispettando i vincoli di
cui sopra. Naturalmente, ogni passeggero pesa molto meno 1000 kg!
Scrivere un algoritmo efficiente che, dato in input l'array p[1..n] di numeri reali, determina il
numero minimo di viaggi che sono necessari per trasportare tutte le persone a destinazione (si
considerino solo i viaggi di andata).
[punti 4]
Determinare il costo asintotico dell'algoritmo proposto, motivando la risposta
[punti 2]
Soluzione.
integer algoritmo FUNIVIA( double p[1..n] )
int nv ← 0;
// numero viaggi
int i ← 1;
while ( i ≤ n ) do
double Cres ← 1000; // capacità residua della cabina
int nres ← 10;
// numero residuo di passeggeri
while ( i ≤ n and Cres ≥ p[i] and nres > 0 ) do
Cres ← Cres – p[i];
nres ← nres – 1;
i ← i + 1;
endwhile
nv ← nv + 1;
endwhile
return nv;
Costo Θ(n)
Nome e Cognome ______________________________________ Matricola __________________
Esercizio 5. Un investitore dispone di S euro (valore intero positivo) che intende investire per un
certo periodo di tempo, ad esempio 10 anni. A questo scopo gli vengono proposte n quote di fondi
di investimento; egli può acquistare oggi la quota i-esima per C[i] euro (valori interi positivi),
ottenendo fra 10 anni un guadagno netto di G[i] euro. L'investitore deve quindi scegliere un
opportuno sottoinsieme delle n quote, rispettando i vincoli seguenti: (i) ogni quota può essere
acquistata al più una volta; (ii) il costo totale delle quote acquistate deve essere minore o uguale a S;
(iii) il guadagno totale deve essere massimo possibile. Più formalmente, è chiesto di determinare un
opportuno sottoinsieme di quote tali che il loro costo sia minore o uguale a S, e il loro guadagno
netto sia massimo possibile.
1. Scrivere un algoritmo efficiente per determinare il massimo guadagno complessivo che è
possibile ottenere (per risolvere il problema in modo corretto è necessario usare la
programmazione dinamica).
[punti 4]
2. Determinare il costo asintotico dell'algoritmo proposto.
[punti 2]
Soluzione. Algoritmo dello zaino basato sulla programmazione dinamica.
Nome e Cognome ______________________________________ Matricola __________________
Esercizio 6. Determinare l'albero dei cammini di costo minimo nel seguente grafo orientato pesato,
utilizzando il nodo s come sorgente. Scrivere all'interno di ciascun nodo la sua distanza dalla
sorgente, ed evidenziare gli archi che fanno parte dell'albero dei cammini di costo minimo. Quale
algoritmo tra quelli visti a lezione utilizzate per determinare i cammini di costo minimo? [punti 4]
4
5
3
-2
-4
4
-8
s
6
3
12
Soluzione. Occorre usare l'algoritmo di Bellman-Ford, perché sono presenti archi di peso negativo.
L'albero risultante dei cammini minimi è il seguente:
4
5
5
3
9
-8
-2
7
s
4
-4
6
3
1
12
3
4
L'algoritmo di Dijkstra non può essere utilizzato, perché ci sono archi di peso negativo. Non è
possibile sommare un peso costante per eliminare i pesi negativi: questo è riportato nei lucidi per
mostrare che non produce il risultato corretto!!!
L'algoritmo di Prim non può essere utilizzato, perché serve a calcolare un Minimum Spanning
Tree, che è diverso dall'albero dei cammini minimi.