Algoritmi e Strutture Dati II ... L'algoritmo che calcola i valori c[1],c[2],...c[n] `e
facilmente ricavabile .... L'algoritmo che calcola i valori di c[i, j] `e il seguente.
Algoritmi e Strutture Dati II Esercizi di Programmazione Dinamica Prof. Paola Bonizzoni Anno Accademico 2007/2008 Appunti scritti da Alberto Leporati, Rosalba Zizza e Christian Pirovano
Esercizio 1 Si determini un algoritmo per trovare in tempo O(n2 ) la pi` u lunga sottosequenza monotona (crescente) di una sequenza di n numeri interi. Per esempio, sia X = 2, 4, 7, 6, 11, 13, 21, 14, 1. Allora la pi` u lunga sottosequenza monotona crescente `e 6, 11, 13, 21, di lunghezza 4. Soluzione. Sia X = x1 , . . . , xn la sequenza di numeri interi; per ogni i ∈ {1, 2, . . . , n}, indichiamo con Xi la sottosequenza x1 , . . . , xi . Sempre per i ∈ {1, 2, . . . , n}, sia c[i] la lunghezza della pi` u lunga sequenza crescente di Xi che termina con xi (cio`e che contiene effettivamente xi ). Il motivo per cui imponiamo che l’elemento xi appartenga effettivamente alla sottosoluzione ottima relativa alla sottosequenza Xi `e che quando andiamo a considerare un elemento xj con j > i, per sapere se tale elemento pu`o far parte della sottosoluzione ottima relativa alla sottosequenza Xj dobbiamo verificare che la condizione xi < xj sia verificata; questo chiaramente lo possiamo fare solo se sappiamo qual `e l’ultimo elemento della sottosequenza crescente pi` u lunga contenuta in Xi . Dovendo quindi memorizzare da qualche parte questa informazione, la cosa migliore `e quella di incorporarla (come abbiamo fatto sopra) nel valore di c[i]. D’altra parte, osserviamo che se l’elemento xi non appartenesse alla pi` u lunga sottosequenza crescente contenuta in Xi vorrebbe dire che esiste un elemento xk , con k < i, che termina tale sequenza. Ma allora tale sequenza sarebbe anche la pi` u lunga sottosequenza crescente contenuta in Xk ; quindi, tanto vale definire il valore di c[i] come abbiamo fatto sopra. Ricaviamo ora un’equazione di ricorrenza che ci consenta di calcolare il ` facile vedere che vale c[1] = 1. valore di c[i] per ogni i ∈ {1, 2, . . . , n}. E Infatti, le sottosequenze possibili di X1 (che `e formata solamente da x1 ) sono due: quella che contiene x1 e quella che non lo contiene. Entrambe sono 1
crescenti, e hanno rispettivamente lunghezza 1 e 0. Quindi la pi` u lunga sottosequenza crescente di X1 ha lunghezza 1, e pertanto poniamo c[1] = 1. Sia ora i > 1, e supponiamo di aver gi`a calcolato i valori di c[1], c[2], . . ., c[i − 1]. Poich´e le sottosequenze di X1 , X2 , . . . , Xi−1 possono essere tutte considerate sottosequenze di Xi−1 , abbiamo che i valori c[1], c[2], . . . , c[i − 1] rappresentano le lunghezze delle pi` u lunghe sottosequenze crescenti di Xi−1 che terminano, rispettivamente, con x1 , x2 , . . . , xi−1 . Tra queste ci saranno alcune sottosequenze alle quali possiamo attaccare xi e altre alle quali l’elemento xi non pu`o essere attaccato. Se prendiamo la pi` u lunga sottosequenza alla quale possiamo attaccare xi , e aggiungiamo xi , otteniamo la pi` u lunga sottosequenza crescente di Xi che termina con xi . La lunghezza di tale sottosequenza sar`a uguale a 1 pi` u la lunghezza della sottosequenza alla quale abbiamo attaccato xi . Pertanto il valore di c[i] `e dato da: ( 1 + max{c[j] | 1 ≤ j < i, xj < xi } se i > 1 c[i] = 1 se i = 1 Poich´e pu`o accadere che l’insieme {c[j] | 1 ≤ j < i, xj < xi } sia vuoto (il che corrisponde al fatto che l’elemento xi `e minore di tutti gli elementi precedenti, e quindi non pu`o essere attaccato a nessuna delle sottosequenze di Xi−1 ), assumiamo per definizione che sia max ∅ = 0, cos`ı che il corrispondente valore di c[i] sia uguale a 1. Una volta calcolati i valori c[1], c[2], . . . , c[n], la soluzione al problema proposto `e data da: max c[i] 1≤i≤n
che `e facilmente ricavabile da un semplice ciclo for che scandisce il vettore c[1..n] alla ricerca del massimo elemento. L’algoritmo che calcola i valori c[1], c[2], . . . c[n] `e facilmente ricavabile dall’equazione di ricorrenza, ed `e il seguente: Max-Growing-Sequence c[1] ← 1 for i ← 2 to n do max ← 0 for j ← 1 to i − 1 do if (xj < xi ) and (c[j] > max) then max ← c[j] 2
endif endfor c[i] ← 1 + max endfor return c ` facile verificare che la complessit`a in tempo dell’algoritmo proposto `e E O(n2 ), mentre lo spazio richiesto `e quello necessario per memorizzare il vettore c[1..n], e quindi Θ(n).
Esercizio 2 Siano date n scatole B1 , . . . , Bn . Ogni scatola Bi `e descritta da una tripla (ai , bi , ci ), le cui componenti denotano rispettivamente lunghezza, larghezza e altezza della scatola. La scatola Bi pu`o essere inserita nella scatola Bj se e solo se ai < aj , bi < bj e ci < cj ; in particolare, le scatole non possono essere ruotate. Per brevit`a indichiamo con Bi ⊂ Bj il fatto che la scatola Bi pu`o essere inserita nella scatola Bj . Descrivere un algoritmo efficiente per determinare il massimo valore di k tale che esiste una sequenza Bi1 , . . . , Bik che soddisfa le condizioni: Bi1 ⊂ Bi2 ⊂ · · · ⊂ Bik e i1 < i2 < · · · < ik Analizzare la complessit`a dell’algoritmo proposto. Soluzione. Nonostante questo esercizio sembri pi` u complicato di quello precedente, il problema proposto `e isomorfo a quello trattato nell’esercizio precedente. Si tratta infatti di trovare la pi` u lunga sottosequenza crescente di scatole contenuta nella sequenza B1 , . . . , Bn . La tecnica di soluzione di questo problema `e identica a quella adottata nell’esercizio precedente, dove al posto di verificare se due interi xj e xi sono tali che xj < xi andiamo a verificare se due scatole Bj e Bi sono tali che Bj ⊂ Bi , ovvero se aj < ai , bj < b i e cj < c i . Sia allora z[1..n] un vettore di n componenti (non lo chiamiamo c, come abbiamo fatto nell’esercizio precedente, per non confoderci con le altezze delle 3
scatole). Detta z[i] la lunghezza massima di una sottosequenza crescente ad elementi in B1 , . . . , Bi che contiene Bi , calcoliamo i valori z[1], z[2], . . . , z[n] in questo ordine. La soluzione del problema proposto sar`a data dal valore di: max z[i]
1≤i≤n
L’equazione di ricorrenza che consente di ricavare il valore di z[i] `e praticamente identica a quella dell’esercizio precedente: ( 1 + max{z[j] | 1 ≤ j < i, aj < ai , bj < bi , cj < ci } se i > 1 z[i] = 1 se i = 1 dove, come abbiamo osservato nell’esercizio precedente, assumiamo che valga max ∅ = 0. L’algoritmo che calcola i valori di z[1], z[2], . . . , z[n] `e il seguente: Max-Boxes-Chain z[1] ← 1 for i ← 2 to n do max ← 0 for j ← 1 to i − 1 do if (aj < ai ) and (bj < bi ) and (cj < ci ) and (z[j] > max) then max ← z[j] endif endfor z[i] ← 1 + max endfor return z ` facile verificare che la complessit`a in tempo dell’algoritmo proposto E `e O(n2 ), mentre lo spazio richiesto `e quello necessario per memorizzare il vettore z[1..n], e quindi Θ(n).
Esercizio 3 Scrivere un algoritmo efficiente che, date due sequenze di interi positivi X = x1 , . . . , xn e Y = y1 , . . . , ym , determini la lunghezza della pi` u lunga 4
sottosequenza crescente comune a X e a Y , ovvero: max{k | ∃ i1 , i2 , . . . , ik ∈ {1, 2, . . . , n}, j1 , j2 , . . . , jk ∈ {1, 2, . . . , m} : i 1 < i 2 < · · · < i k , j1 < j 2 < · · · < j k , xi1 = yj1 < xi2 = yj2 < . . . < xik = yjk } Ad esempio, se X = 1, 4, 12, 3, 7, 16, 8 e Y = 12, 1, 3, 17, 8 allora la lunghezza della pi` u lunga sottosequenza crescente comune a X e a Y `e 3 (corrispondente a 1, 3, 8), mentre 12, 3, 8 `e una LCS ma non `e crescente. Soluzione. La soluzione di questo esercizio `e molto simile a quelle date per gli esercizi precedenti. Come al solito, indichiamo con Xi la sottosequenza x1 , x2 , . . . , xi di X, e con Yj la sottosequenza y1 , y2 , . . . , yj di Y . I sottoproblemi naturali del problema proposto consistono nel determinare la lunghezza di una LCS crescente tra Xi e Yj , per i ∈ {1, . . . , n} e j ∈ {1, . . . , m}. Come abbiamo fatto negli esercizi precedenti, per essere sicuri che la soluzione al problema sia valida (cio`e che la LCS di cui calcoliamo la lunghezza sia crescente) occorre memorizzare da qualche parte il valore dell’ultimo elemento delle sottosequenze ottime corrispondenti ai sottoproblemi. Conviene pertanto definire una matrice c[1..n, 1..m], dove il valore di c[i, j] `e la lunghezza di una LCS crescente tra Xi e Yj tale che l’ultimo elemento della LCS `e uguale sia a xi che a yj (e quindi, in particolare, xi = yj ). Ricaviamo l’equazione di ricorrenza che consente di determinare il valore di c[i, j]. Se xi 6= yj , non esiste una LCS crescente tra Xi e Yj che termina sia con xi che con yj ; pertanto, in questo caso abbiamo c[i, j] = 0. Se invece xi = yj , occorre cercare la pi` u lunga tra le LCS crescenti di Xs e Yt , per tutti i valori di s e t tali che 1 ≤ s < i e 1 ≤ t < j, che terminano con un valore minore di xi . Detti s e t i valori di s e t cos`ı individuati1 , il valore di c[i, j] sar`a uguale a 1 + c[s, t]. Quindi, l’equazione di ricorrenza che consente di ricavare c[i, j] `e la seguente: ( 0 se xi 6= yj c[i, j] = 1 + max{c[s, t] | 1 ≤ s < i, 1 ≤ t < j, xs ≤ xi } se xi = yj dove, come abbiamo fatto negli esercizi precedenti, assumiamo che valga max ∅ = 0. 1
s e t sono cio´e i valori massimi tra gli s e t per cui valgono le disequazioni 1 ≤ s < i e 1≤t max then max ← c[s, t] endif endfor endfor c[i, j] ← 1 + max endif endfor endfor return c ` facile verificare che la complessit`a in tempo dell’algoritmo proposto `e E O((nm)2 ), mentre lo spazio richiesto `e quello necessario per memorizzare il vettore c[1..n, 1..m], e quindi Θ(nm). Di seguito vediamo la matrice c ottenuta con le stringhe X = 1, 4, 12, 3, 7, 16, 8 e Y = 12, 1, 3, 17, 8 (sulle intesazioni di righe e colonne, mettiamo direttamente i valori di xi e yj ). In questo caso l’ottimo lo si trova in posizione c[7, 5], in generale lo si deve cercare come massimo elemento in tutta la matrice.
6
1 4 12 3 7 16 8
12 0 0 1 0 0 0 0
1 1 0 0 0 0 0 0
3 17 8 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 3
Tabella 1: Matrice c, con le pi´ u lunghe LCS crescenti tra xi e yj
Esercizio 4 Si dice che una sequenza di interi x1 , x2 , . . . , xm `e una sequenza zig–zag se, per ogni i ∈ {1, 2, . . . , m − 1}, vale: xi < xi+1 xi > xi+1
se i `e dispari se i `e pari
Ad esempio, 3, 8, 1, 5, 2 `e una sequenza zig–zag, mentre 3, 8, 10, 5, 2 non `e una sequenza zig–zag (10 ´e maggiore di 8). Descrivere e analizzare un algoritmo che, data una sequenza Y = y1 , y2 , . . . , ym , determini la lunghezza della pi` u lunga sottosequenza zig–zag di Y . Ad esempio, se Y = 3, 4, 8, 5, 6, 2 allora la lunghezza massima `e 5 (corrispondente alla sottosequenza 3, 8, 5, 6, 2, o anche 4, 8, 5, 6, 2). Soluzione. 1. Data Y = y1 y2 · · · yn , sia x1 · · · xm la pi` u lunga sottosequenza ZIGZAG di Y , dove xm = yt , con 1 ≤ t ≤ n (ossia t `e l’indice dell’ultimo elemento preso effettivamente nella sequenza ZIG-ZAG). Se |X| `e pari, ossia m `e pari, allora x1 · · · xm−1 deve essere la pi` u lunga sottosequenza ZIG-ZAG di lunghezza dispari di y1 · · · yk , con k < t e yk < yt . Se cos`ı non fosse, esisterebbe un’altra sottosequenza di lunghezza dispari, pi` u lunga di x1 · · · xm−1 , con ultimo elemento minore di yt tale che aggiungendo yt , otterrei una sottosequenza ZIG-ZAG di Y di lunghezza maggiore di x1 · · · xm , il che `e assurdo, dato che x1 · · · xm era la soluzione ottima. Simmetricamente se m `e dispari. 2. Definiamo Z(i, 0) la lunghezza della pi` u lunga sottosequenza a ZIGZAG di lunghezza pari di y1 · · · yi , che termina con yi . Similmente, definiamo Z(i, 1) la lunghezza della pi` u lunga sottosequenza a ZIG-ZAG di lunghezza 7
´ importante notare che yi deve dispari di y1 · · · yi , che termina con yi . E essere l’ultimo elemento della sequenza a ZIG-ZAG di y1 · · · yi , altrimenti non sarebbe possibile verificare la natura a ZIG-ZAG della sequenza stessa. Allora, si ha 1 + max{Z(k, 1)|1 ≤ k < i, yk < yi } se i ≥ 2 0 se i = 1 oppure Z(i, 0) = 6 ∃k t.c. 1 ≤ k < i, yk < yi . 1 + max{Z(k, 0)|1 ≤ k < i, yk > yi } se i ≥ 2 1 se i = 1 oppure Z(i, 1) = 6 ∃k t.c. 1 ≤ k < i, yk > yi . La soluzione si ha calcolando max {Z(i, 0), Z(i, 1)}
1≤i≤n
3. Di seguito lo pseudocodice che implementa le equazioni di programmazione dinamica trovate. Z(1,0):=0; Z(1,1)=1; massimo:=0; for i:=1 to n do begin for k:=i-1 downto to 1 do begin max1:=-1; max2:=0; if yk < yj then begin q:=Z(k,1); if q> max1 then max1:=q end else begin q:=Z(k,0); if q>max2 then max2 :=q end end 8
Z(i,0):=1+max1; Z(i,1):=1+max2; if (massimo < max{Z(i, 0), Z(i, 1)}) then massimo := max{Z(i, 0), Z(i, 1)} end return massimo
Riportiamo di seguito un esempio di Z per Y = 3, 4, 8, 5, 6, 2. Z(0) Z(1)
0 2 2 1 1 1
2 4 0 3 3 5
Esercizio 5 Siano G1 = (V, E1 ) e G2 = (V, E2 ) due grafi diretti definiti sullo stesso insieme di vertici V = {v1 , v2 , . . . , vn }, i cui archi sono del tipo (vi , vj ), con 1 ≤ i < j ≤ n. Ad ogni arco (u, v) ∈ Ei , i = 1, 2, `e associato un costo ci (u, v), intero e strettamente positivo. Un cammino (vi1 , vi2 , . . . , vik ), vij ∈ V , `e detto alternante se alterna archi di E1 ad archi di E2 , ovvero se (vij , vij+1 ) ∈ E1 per ogni j dispari, e (vij , vij+1 ) ∈ E2 per ogni j pari. Scrivere l’equazione di ricorrenza per determinare, dati G1 , G2 , il cammino alternante di costo minimo da v1 a vn . Possibile soluzione. (a) 1. Supponiamo di aver trovato un cammino tra i vertici (v1 , . . . , vn ), che minimizza il costo totale e che sia alternante. L’ultimo arco pu`o essere di E1 o di E2 . La soluzione ottima sar`a data dal minimo tra il cammino che termina con un arco di E1 e quello che termina con arco di E2 . Di conseguenza, il cammino minimo alternante sar`a dato dalla somma del costo del cammino minimo su (v1 , . . . , vn−1 ) che termina con arco di E2 e il costo dell’arco di E1 che incide su vn nel primo caso, e viceversa nel secondo caso. Se questa non fosse ottima per n − 1 vertici, potrei trovarne un’altra ottima per n vertici aggiungendo a questa il costo dell’ultimo arco (assurdo). 2. Definiamo M (i, 1) = costo minimo di un cammino alternante da v1 a vi , dove l’ultimo arco considerato `e di E1 e M (i, 2) = costo minimo di un cammino alternante da v1 a vi , dove l’ultimo arco considerato `e di E2 . 9
Indicheremo con ck (j, i) il costo dell’arco (vj , vi ) nel grafo Gk , per k = 1, 2. La ricorrenza `e M (i, 1) = min {M (j, 2) + c1 (j, i)} 1≤j −∞ then return M(i,j,t); if ((t=0) or (i>n) or (j>m)) then return 0 else if t > min{n − i + 1, m − j + 1} then return −∞ else begin q1 :=Lookup(i+1,j+1,t-1) + c(i,j); q2 :=Lookup(i,j+1,t); q3 :=Lookup(i+1,j,t); q:= max{q1 , q2 , q3 }; if q>M(i,j,t) then M(i,j,t):=q; end; return M(i,j,t). Scriviamo ora un algoritmo con tecnica bottom-up (iterativa). CoppieNonIncrocianti(n,m,k); for i:=n downto 1 do for j:=m downto to 1 do for t:=0 to k do begin if ((t=0) or (i>n) or (j>m)) then M(i,j,t):=0 else if t > min{n − i + 1, m − j + 1} then return −∞ else begin q1 :=Lookup(i+1,j+1,t-1) + c(i,j); q2 :=Lookup(i,j+1,t); q3 :=Lookup(i+1,j,t); q:= max{q1 , q2 , q3 }; 15
if q>M(i,j,t) then M(i,j,t):=q; end; end; return M(1,1,k).
Esercizio 9 Sia G = (V, E) un grafo orientato e aciclico, in cui ogni arco (u, v) ∈ E ha un costo Cu,v > 0. Il costo misto di un cammino (u0 , u1 ), (u1 , u2 ), . . . , (uk−1 , uk ) di lunghezza k `e k+
k−1 X
(−1)i Cui ,ui+1
i=0
Descrivere una equazione di ricorrenza che, data un grafo G orientato e aciclico, un insieme di costi {Cu,v } e un vertice w ∈ V , determini il massimo costo misto di un cammino che parte da w. Possibile soluzione. 1. Supponiamo di avere determinato il massimo costo misto di un cammino che parte da w e sia (w, ui1 , . . . , uin ) tale cammino associato alla soluzione ottima. Se consideriamo il cammino (w, ui1 , . . . , uin−1 ), esso deve rappresentare un cammino di massimo costo misto di lunghezza k − 1, altrimenti perderei l’ottimalit`a della soluzione (di lunghezza k). Infatti, se al costo misto ottimale aggiungessi (se k `e dispari, sia ha k − 1 pari e dunque l’ultimo costo `e aggiunto al parziale) il costo dell’ultimo arco Cin−1 ,in , o lo togliessi (se k `e pari, allora k − 1 ´e dispari e dunque l’ultimo costo `e sottratto al parziale), otterrei una soluzione finale migliore di quella fissata (assurdo). 2. Definiamo M (v, 0) = il massimo costo misto del cammino da w a v di lunghezza pari, e M (v, 1) = il massimo costo misto del cammino da w a v di lunghezza dispari. La ricorrenza `e: M (v, 0) = max {M (u, 1) − Cu,v + 1} u:(u,v)∈E
M (v, 1) = max {M (u, 0) + Cu,v + 1} u:(u,v)∈E
e inoltre M (w, 0) = 0, M (w, 1) = −∞ e M (v, 0) = M (v, 1) = −∞, se non esiste arco u tale che (u, v) ∈ E. La soluzione al problema sar`a 16
max{M (v, 0), M (v, 1)} v∈V
Esercizio 10 Sia T un albero binario in cui ad ogni arco (u, v) `e associato un peso w(u,v) > 0. Una (0,1)-colorazione di T associa ad ogni arco uno dei due colori 0 e 1, in modo tale che tutti gli archi, quando in numero di tre, incidenti su uno stesso nodo non hanno lo stesso colore. Il peso di una (0,1)-colorazione `e la somma dei pesi degli archi colorati 1. Descrivere una ricorrenza che dato in input un albero T e i pesi w(u,v) > 0 relativi ad ogni arco (u, v) in T, determini il massimo peso di una (0,1)colorazione. Possibile soluzione. 1. Una soluzione ottima al problema `e una (0,1)colorazione che massimizza il peso dell’albero radicato in root(T ). Se si etichetta il ramo tra root(T ) e un suo figlio U con 0 (risp. 1), allora entrambi i rami tra U e i suoi figli non possono essere etichettati 0 (risp.1). Sceglier`o la configurazione che massimizza il peso totale e aggiunger`o il peso dell’arco solo se l’etichetto 1. Se il problema richiamato sui figli di root(T ) non desse soluzione ottima, ne potrei ottenere un’altra migliore per il problema originario. 2. Definiamo M ((u, v), c) = il massimo peso di una (0,1)-colorazione del sottoalbero contenente l’arco (u, v) e tutti gli archi del sottoalbero radicato in v, quando l’arco (u, v) `e colorato c, con c ∈ {0, 1}. La ricorrenza `e: M ((v, lef t(v)), 0) + M ((v, right(v)), 1) M ((u, v), 0) = max M ((v, lef t(v)), 1) + M ((v, right(v)), 1) M ((v, lef t(v)), 1) + M ((v, right(v)), 1). M ((v, lef t(v)), 0) + M ((v, right(v)), 0) M ((u, v), 1) = w(u,v) + max M ((v, lef t(v)), 0) + M ((v, right(v)), 1) M ((v, lef t(v)), 1) + M ((v, right(v)), 0). Inoltre M ((u, N IL), 0) = M ((u, N IL), 1) = 0. La soluzione al problema `e data da 17
M ((root(T ), lef t(root(T ))), 0) + M ((root(T ), right(root(T ))), 0) M ((root(T ), lef t(root(T ))), 0) + M ((root(T ), right(root(T ))), 1) max M ((root(T ), lef t(root(T ))), 1) + M ((root(T ), right(root(T ))), 0) M ((root(T ), lef t(root(T ))), 1) + M ((root(T ), right(root(T ))), 1).
Esercizio 11 Sia G = (V, E, ω) un grafo con archi E = E1 ∪ E2 , con E1 ed E2 disgiunti. Si determino le equazioni di ricorrenza di un algoritmo di programmazione dinamica per il calcolo del costo minimo di un cammino dal vertice i al vertice j contenente al pi` u z archi di E1 . Possibile soluzione. Per la soluzione di questo esercizio, modifichiamo l’equazione di ricorrenza dell’algoritmo di Floyd-Warshall, aggiungendo un parametro. Ricordiamo che V = {1, . . . , n} e che il grafo in input ha una funzione peso ω cos`ı definita: 0 se i = j ∞ se i 6= j, (i, j) 6∈ E wij = cij se i 6= j, (i, j) ∈ E. dove cij `e il costo effettivo dell’arco (i, j). Ricordando la ricorrenza di FloydWarshall, aggiungiamo un parametro che tenga conto degli archi di E1 , su cui abbiamo un vincolo. Dunque la ricorrenza pu`o essere cos`ı descritta. Indico con D(i, j, k, t) il costo minimo di un cammino da i a j con vertici intermedi in {1, . . . , k}, usando al pi` u t archi di E1 . Infatti ad ogni passo, posso non considerare il vertice intermedio k, e quindi riconduco il mio problema a quello del calcolo del costo minimo di un cammino da i a j con vertici intermedi in {1, . . . , k − 1}, usando al pi` ut archi di E1 . Se, invece, considero il vertice k, spezzo il cammino da i a j in due, uno da i a k, l’altro da k a j usando in entrambi i cammini i vertici in {1, . . . , k − 1}; inoltre, poich`e in totale si devono avere al pi` u t archi di E1 , si considerano tutte le possibili espressioni di t come somma di due valori, uno per ogni pezzo del cammino. Precisamente:
18
½ D(i, j, k, t) = min
D(i, j, k − 1, t) mint1 +t2 ≤t {D(i, k, k − 1, t1 ) + D(k, j, k − 1, t2 )}
se (a) se (b)
dove (a) k non appartiene al cammino da i a j, (b) k appartiene al cammino da i a j. Se k = 0 e t = 0, allora D(i, j, 0, 0) = ∞, se (i, j) ∈ E1 , mentre D(i, j, 0, 0) = ωij , se (i, j) 6∈ E1 . Inoltre se k = 0, t > 0, allora D(i, j, 0, t) = ωij . La soluzione si ottiene con la chiamata a D(i, j, n, z).
Esercizio 12 Si costruisca la ricorrenza di un algoritmo di programmazione dinamica che determini, in un grafo colorato con vertici rossi e neri, il cammino minimo dal nodo i al nodo j contenente esattamente k vertici rossi interni al cammino (ossia esclusi i e j). Possibile soluzione. Per la soluzione di questo esercizio, modifichiamo l’equazione di ricorrenza dell’algoritmo di Floyd-Warshall, aggiungendo un parametro. Ricordiamo che V = {1, . . . , n} e che il grafo in input ha una funzione peso ω cos`ı definita: 0 se i = j ∞ se i 6= j, (i, j) 6∈ E wij = cij se i 6= j, (i, j) ∈ E. dove cij `e il costo effettivo dell’arco (i, j). Ricordando la ricorrenza di FloydWarshall, aggiungiamo un parametro che tenga conto del vincolo sul numero di vertici rossi. Dunque la ricorrenza pu`o essere cos`ı descritta. Indico con D(i, j, k, l) il costo minimo di un cammino da i a j con vertici intermedi in {1, . . . , l}, contenente esattamente k vertici (interni) rossi. Infatti ad ogni passo, posso non considerare il vertice intermedio l e quindi riconduco il problema a quello del calcolo del costo minimo di un cammino da i a j con vertici intermedi in {1, . . . , l − 1}, contenente esattamente k vertici interni rossi. Se, invece, considero il vertice l, spezzo il cammino da 19
i a j in due cammini minimi, uno minimo da i a l, l’altro minimo da l a j usando in entrambi i cammini i vertici in {1, . . . , l − 1}; inoltre, poich`e in totale si devono avere esattamente k vertici rossi intermedi, si considerano tutte le possibili espressioni di k come somma di due valori k1 e k2 , uno per ogni pezzo del cammino. Per capire come questi sono legati al valore k del problema, bisogna tener conto del fatto che se il vertice intermedio l `e nero, si ha che k1 + k2 = k, mentre se l `e rosso, si ha che k1 + k2 = k − 1, essendo l intermedio e colorato di rosso, ma che non viene contato nei due sottoproblemi (che escludono dal conteggio i vertici estremi di ogni cammino). Precisamente, per ogni l, k > 0: se (1) D(i, j, k, l − 1) se (2) D(i, j, k, l) = min mink1 +k2 =k {D(i, l, l − 1, k1 ) + D(l, j, l − 1, k2 ))} mink1 +k2 +1=k {D(i, l, l − 1, k1 ) + D(l, j, l − 1, k2 ))} se (3) . dove (1) l non appartiene al cammino, (2) l appartiene al cammino, l nero, (3) l appartiene al cammino, l rosso. Inoltre, se non ci sono vertici intermedi, ossia sto considerando l’arco (i, j), allora se non richiedo vertici rossi intermedi, allora il valore del sottoproblema sar`a esattamente il peso dell’arco, mentre se richiedo un certo numero di vertici rossi k > 0, allora ho una condizione di errore. Per impedire che tale sottoproblema sia allora considerato, attribuisco come valore infinito. Infine, se non devo considerare pi` u alcun vertice intermedio rosso, avr`o condizione di errore se l intermedio `e rosso, perch´e tale situazione non deve essere scelta. Allora, ½ wij se k = 0 D(i, j, k, 0) = ∞ se k > 0. se (1) D(i, j, 0, l − 1) D(i, l, 0, l − 1) + D(l, j, 0, l − 1)) se (2) D(i, j, 0, l) = ∞ se (3) . La soluzione si ottiene con la chiamata a D(i, j, k, n).
20
Esercizio 13 Si costruisca la ricorrenza di un algoritmo di programmazione dinamica che determini, in un grafo colorato con vertici rossi e neri, il cammino minimo dal nodo i al nodo j contenente esattamente k vertici rossi inclusi i e j. Possibile soluzione. Tale esercizio `e una delle possibili variazione dell’esercizio 11. Infatti, indichiamo con D(i, j, k, l) il costo minimo di un cammino da i a j con vertici intermedi in {1, . . . , l}, contenente esattamente k vertici rossi compresi i e j. Allora, seguendo il ragionamento precedente, se il vertice intermedio `e nero, allora la ricorrenza resta inalterata, ma se l `e rosso, verr`a conteggiato in entrambi i sottoproblemi che rappresentano i due sottocammini in cui il cammino da i a j viene spezzato rispetto a l. Allora, il minimo sar`a fatto su k1 + k2 = k + 1. Inoltre, la base della ricorsione resta identica per k = 0. Invece, se non ci sono vertici intermedi, dunque l = 0, si ha D(i, j, k, 0) = wij se k = 0 e i, j entrambi neri, oppure k = 1 e solo una tra i, j rosso, oppure se k = 2 e i, j entrambi rossi (in altre parole, se k `e esattamente il numero di rossi tra i e j). Invece, D(i, j, k, 0) = ∞ se k > 0 e i, j entrambi neri, oppure k = 0 o k ≥ 2 e solo una tra i, j rosso, oppure se k ≤ 1 e i, j entrambi rossi (in altre parole, se k `e diverso dal numero di rossi tra i e j).
Esercizio 14 (Compito del 9.7.2001) Si costruisca la ricorrenza di un algoritmo di programmazione dinamica che determini in un grafo G colorato con vertici rossi e neri se esiste un cammino dal nodo x al nodo y contenente al pi` u k vertici neri interni al cammino. Possibile soluzione. Sia G = (V, E), dove V = {1, . . . , n} `e l’insieme dei vertici V = {1, . . . , n} ognuno colorato rosso o nero ed E `e l’insieme degli archi di G. Indichiamo con D(i, j, t, k) l’espressione booleana associata all’esistenza di un cammino dal vertice i al vertice j passando per i vertici intermedi in {1, . . . , t} ed usando al pi` u k vertici neri interni al cammino. Dunque D(i, j, t, k) = 1 se esiste cammino da i a j passando per i vertici intermedi in {1, . . . , t} ed usando al pi` u k vertici neri interni al cammino, altrimenti D(i, j, t, k) = 0.
21
Infatti ad ogni passo, posso considerare il vertice intermedio t. Se t non appartiene al cammino tra i e j, allora riconduco il problema a quello dell’esistenza di un cammino da i a j con vertici intermedi in {1, . . . , t − 1}. Se invece t appartiene al cammino tra i e j spezzo il cammino da i a j in due, uno da i a t, l’altro da t a j usando in entrambi i cammini i vertici in {1, . . . , t − 1}; inoltre, poich`e in totale si devono avere al pi` u k vertici neri, se t `e nero si considerano tutte le possibili espressioni di k − 1 come somma di due valori, uno per ogni pezzo del cammino, se invece t `e rosso, considero le espressioni di k come somma di due valori. Precisamente, per ogni k > 0, definiamo se t 6∈ cammino da i a j, D(i, j, t − 1, k) D(i, t, t − 1, k1 ), D(t, j, t − 1, k2 ) se t rosso, k1 + k2 ≤ k, D(i, j, t, k) = D(i, t, t − 1, k1 ), D(t, j, t − 1, k2 ) se t nero, k1 + k2 + 1 ≤ k. dove la parentesi { significa “OR” tra le tre possibilit`a e la virgola tra le due condizioni significa “AND”. Le condizioni di base sono le seguenti. Se t = 0, allora D(i, j, 0, k) = 1, se (i, j) ∈ E, mentre D(i, j, 0, k) = 0, se (i, j) 6∈ E, per ogni k ≥ 0. Infine, D(i, j, t, 0) = D(i, j, t − 1, 0) se t non appartiene al cammino da i a j, mentre, per ogni t, D(i, j, t, 0) = 0, se t nero, D(i, j, t, 0) = D(i, j, t − 1, 0), se t rosso. La soluzione si ottiene con la chiamata a D(x, y, n, k).
22