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