Burst Balloons: DP sugli intervalli al contrario
Risolva il problema burst-balloons ragionando al contrario: scelga l'ultimo palloncino da far scoppiare in ogni intervallo invece del primo.
Burst Balloons: DP sugli intervalli al contrario è una lezione DSA Interview Prep gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento DSA Interview Prep, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso DSA Interview Prep include 4 lezioni in totale.
Il problema Burst Balloons
Data una serie di n palloncini con valori nums, far scoppiare il palloncino i produce nums[i-1] * nums[i] * nums[i+1] monete (il prodotto del palloncino stesso e dei suoi vicini attuali). Dopo lo scoppio, i vicini diventano adiacenti. L'obiettivo è trovare il numero massimo di monete ottenibile facendo scoppiare tutti i palloncini. La simulazione ingenua è difficile perché far scoppiare un palloncino modifica i vicini: la DP inversa su intervalli evita elegantemente questa difficoltà.
Perché la simulazione in avanti non funziona
Se si definisse dp[i][j] come il numero massimo di monete ottenibile facendo scoppiare i palloncini nell'intervallo [i, j] e si considerasse quale palloncino far scoppiare per primo, sorgerebbe un problema: far scoppiare per primo il palloncino k significa che nums[k-1] e nums[k+1] devono essere i vicini attuali, ma questi palloncini potrebbero essere fatti scoppiare in seguito, modificando dinamicamente i vicini. È difficile definire lo stato in modo chiaro procedendo in avanti.
L'idea chiave: ragionare al contrario
Il trucco consiste nel ragionare su quale palloncino viene fatto scoppiare per ultimo nell'intervallo [i, j]. Quando il palloncino k è l'ultimo a scoppiare in [i, j], tutti gli altri palloncini in [i, j] sono già scomparsi. I vicini del palloncino k sono quindi esattamente nums[i-1] e nums[j+1], cioè i palloncini di confine appena fuori dall'intervallo. Questo rende deterministico il calcolo delle monete dell'ultimo scoppio: non dipende dall'ordine degli scoppi precedenti.
Definizione dello stato e della ricorrenza
Si aggiungano i palloncini sentinella: si anteponga e si aggiunga 1 a nums per ottenere nums = [1] + nums + [1]. Si definisca dp[i][j] come il numero massimo di monete ottenibile facendo scoppiare tutti i palloncini strettamente compresi tra gli indici i e j (estremi esclusi), dove nums[i] e nums[j] sono i palloncini di confine che rimangono. Ricorrenza: per ogni possibile ultimo palloncino k in (i, j): dp[i][j] = max(dp[i][k] + dp[k][j] + nums[i]*nums[k]*nums[j]).
# With sentinels: nums = [1] + original + [1]
# dp[i][j] = max coins from bursting all balloons in open interval (i, j)
# k = last balloon to burst in (i,j)
# dp[i][j] = max over k in (i,j): dp[i][k] + dp[k][j] + nums[i]*nums[k]*nums[j]Implementazione completa
Si estende l'array con le sentinelle, si inizializza la tabella DP a zero (un intervallo vuoto vale 0 monete) e si procede per lunghezze crescenti degli intervalli. La risposta finale è dp[0][n+1], che rappresenta il numero massimo di monete ottenibile facendo scoppiare tutti i palloncini originali, con le sentinelle come confini permanenti.
def maxCoins(nums):
nums = [1] + nums + [1]
n = len(nums)
dp = [[0]*n for _ in range(n)]
# length of open interval (i, j) exclusive: j - i - 1 balloons inside
for length in range(2, n): # length = j - i
for i in range(0, n - length):
j = i + length
for k in range(i+1, j): # k is last burst in (i, j)
coins = dp[i][k] + dp[k][j] + nums[i]*nums[k]*nums[j]
dp[i][j] = max(dp[i][j], coins)
return dp[0][n-1]
print(maxCoins([3, 1, 5, 8])) # 167Analisi passo per passo dell'esempio
Per [3, 1, 5, 8], completato con le sentinelle diventa [1, 3, 1, 5, 8, 1] (indici 0-5). Si vuole calcolare dp[0][5]. Per gli intervalli length=2 (un palloncino all'interno): dp[0][2] = 1*3*1=3, dp[1][3]=3*1*5=15, dp[2][4]=1*5*8=40, dp[3][5]=5*8*1=40. Procedendo, la soluzione ottimale consiste nel far scoppiare 1 per ultimo tra {3,1,5,8}, dopo aver fatto scoppiare prima i vicini, ottenendo un totale di 167 monete.
Analisi della complessità
Ci sono O(n²) intervalli e per ciascun intervallo si provano O(n) punti di divisione, ottenendo una complessità temporale O(n³). Lo spazio richiesto dalla tabella DP è O(n²). Per n = 500 palloncini si tratta di 125 milioni di operazioni, un numero gestibile con i vincoli tipici dei colloqui. L'aggiunta delle sentinelle semplifica la gestione dei bordi: senza di esse, sarebbe necessario verificare esplicitamente se i-1 e j+1 rientrano nei limiti.
Alternativa top-down con memoizzazione
La stessa soluzione può essere scritta con un approccio top-down usando @lru_cache, che potrebbe risultare più intuitivo da ricavare durante un colloquio. Si definisca solve(i, j) come il numero massimo di monete nell'intervallo aperto (i, j). La funzione prova ogni k come ultimo palloncino da far scoppiare e memorizza i risultati. Entrambi gli approcci hanno la stessa complessità temporale e spaziale.
from functools import lru_cache
def maxCoins_memo(nums):
nums = [1] + nums + [1]
n = len(nums)
@lru_cache(maxsize=None)
def solve(i, j):
if j - i < 2: # no balloons between i and j
return 0
return max(
solve(i, k) + solve(k, j) + nums[i]*nums[k]*nums[j]
for k in range(i+1, j)
)
return solve(0, n-1)
print(maxCoins_memo([3, 1, 5, 8])) # 167Errore comune: definizione della DP in avanti
Un errore comune consiste nel definire dp[i][j] come il numero di monete ottenibile quando il primo palloncino in [i,j] viene fatto scoppiare, invece dell'ultimo. Questo non funziona perché il calcolo delle monete per il primo scoppio dipende dai palloncini vicini che non sono ancora stati fatti scoppiare, e lo stato di questi vicini cambia man mano che l'algoritmo procede. Nella DP su intervalli, quando i confini dipendono dagli elementi rimanenti, bisogna sempre ragionare sull'ultimo elemento dell'intervallo.
Perché usare valori sentinella pari a 1?
Si scelgono sentinelle di valore 1 perché agiscono da elementi neutri per la moltiplicazione. Quando un palloncino di confine è l'ultimo a scoppiare, il suo valore in monete è boundary * last * boundary = 1 * last * 1 = last. Usare 0 produrrebbe 0 monete (sbagliato), mentre usare altri valori altererebbe il calcolo. Il trucco delle sentinelle unifica in modo pulito tutti i casi ai bordi senza dover gestire separatamente il palloncino più a sinistra e quello più a destra.
Confronto con la DP su intervalli standard
Nella DP su intervalli standard (moltiplicazione a catena di matrici), il punto di divisione k indica dove dividere il problema in due sottoproblemi risolti indipendentemente. In Burst Balloons, k è l'elemento che viene fatto scoppiare per ultimo nell'intervallo, rendendo indipendenti i due sottointervalli [i,k] e [k,j], dato che k è ancora presente come elemento di confine. Questa prospettiva inversa è l'idea creativa che rende risolvibile Burst Balloons con la DP su intervalli.
Verifica rapida
Verifichi la Sua comprensione dei concetti di Data Structures & Algorithms — Coding Interview Prep trattati in questa lezione.
Riepilogo della lezione
In questa lezione ha imparato: la simulazione in avanti fallisce perché l'esplosione dei palloncini modifica i vicini in modo imprevedibile, l'intuizione inversa definisce k come l'ultimo palloncino fatto esplodere in un intervallo, rendendo nums[i] e nums[j] i vicini e la ricorrenza dp[i][j] = max(dp[i][k] + dp[k][j] + nums[i]*nums[k]*nums[j]) con il padding tramite sentinelle fornisce una soluzione O(n³). Ora passeremo alla DP dello zaino, iniziando dal classico zaino 0/1 e dalla sua ottimizzazione dello spazio.
Domande Frequenti
La lezione «Burst Balloons: DP sugli intervalli al contrario» è gratuita?
Sì — il testo completo di «Burst Balloons: DP sugli intervalli al contrario» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso DSA Interview Prep, passa a CoddyKit PRO. Il corso DSA Interview Prep include 4 lezioni in totale.
Cosa imparerò in «Burst Balloons: DP sugli intervalli al contrario»?
Risolva il problema burst-balloons ragionando al contrario: scelga l'ultimo palloncino da far scoppiare in ogni intervallo invece del primo. Eserciti DSA Interview Prep con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare DSA Interview Prep?
Non è richiesta alcuna esperienza precedente. DSA Interview Prep su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Burst Balloons: DP sugli intervalli al contrario»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione DSA Interview Prep?
Sì. Ogni lezione DSA Interview Prep include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Schema della DP sugli intervalli e ordine di riempimento
- Sottosequenza e sottostringa palindroma più lunga
- Partizionamento palindromico II
- Burst Balloons: DP sugli intervalli al contrario