Intervall-DP-mønster og utfyllingsrekkefølge
Definer DP-tilstanden dp[i][j], forklar hvorfor intervaller må fylles ut i stigende lengderekkefølge, og følg mønsteret på matrise-kjedemultiplikasjon.
Intervall-DP-mønster og utfyllingsrekkefølge er en gratis leksjon i Forberedelse til kodeintervjuer på CoddyKit. Dette er leksjon 1 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Forberedelse til kodeintervjuer, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Forberedelse til kodeintervjuer inneholder totalt 4 leksjoner.
Hva er intervall-DP?
Intervall-DP er et mønster for dynamisk programmering der tilstanden dp[i][j] representerer det optimale svaret for delproblemet som dekker indeksene i til og med j. Den viktigste innsikten er at vi først løser mindre intervaller og bygger oss opp til hele området. Dette mønsteret modellerer naturlig problemer som matrisekjedemultiplikasjon, palindrompartisjonering og ballongsprekking, der grensene for delproblemet er venstre og høyre endepunkt i et område.
Tilstandsdefinisjon og basistilfeller
For intervall-DP er tilstanden dp[i][j], der i <= j. Basistilfellene er intervaller med ett element: dp[i][i]. Disse er trivielt løst – for eksempel har én matrise en multiplikasjonskostnad på null. Intervaller med to elementer, dp[i][i+1], har ofte også enkle svar. Vi fyller ut tabellen for økende intervallengder, fra lengde 1 til og med n.
n = 4
dp = [[0] * n for _ in range(n)]
# Base cases: single elements
for i in range(n):
dp[i][i] = 0 # length-1 intervalsFyllingsrekkefølge: økende lengde
Den avgjørende detaljen i intervall-DP er fyllingsrekkefølgen. Vi må beregne alle intervaller med lengde L før vi beregner intervaller med lengde L+1, fordi et lengre intervall avhenger av kortere delintervaller. Den ytre løkken går gjennom intervallengdene fra 2 til n, den midterste løkken setter venstre grense i, og vi utleder høyre grense som j = i + L - 1.
n = 5
dp = [[float('inf')] * n for _ in range(n)]
for i in range(n):
dp[i][i] = 0
for length in range(2, n + 1): # interval length
for i in range(n - length + 1): # left boundary
j = i + length - 1 # right boundary
for k in range(i, j): # split point
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j])Oppsett for matrisekjedemultiplikasjon
Det klassiske intervall-DP-problemet er matrisekjedemultiplikasjon: Gitt matriser med dimensjoner dims[0..n] skal du finne det minste antallet skalære multiplikasjoner som trengs for å beregne produktet. Det koster p*q*r operasjoner å multiplisere matrise A(p×q) med B(q×r). dp[i][j] = den minste kostnaden for å multiplisere matrise i til og med j. Delingspunktet k avgjør hvor sekvensen deles i to delkjeder.
def matrix_chain_order(dims):
n = len(dims) - 1 # number of matrices
dp = [[0] * n for _ in range(n)]
for length in range(2, n + 1):
for i in range(n - length + 1):
j = i + length - 1
dp[i][j] = float('inf')
for k in range(i, j):
cost = dp[i][k] + dp[k+1][j] + dims[i]*dims[k+1]*dims[j+1]
dp[i][j] = min(dp[i][j], cost)
return dp[0][n-1]
print(matrix_chain_order([10, 30, 5, 60])) # 4500Gjennomgang av DP-tabellen
La oss gå gjennom matrisekjedeeksempelet med dimensjonene [10, 30, 5, 60], som representerer tre matriser: A(10×30), B(30×5), C(5×60). For dp[0][2] prøver vi deling ved k=0: dp[0][0] + dp[1][2] + 10×30×60 = 0 + 9000 + 18000 = 27000, og ved k=1: dp[0][1] + dp[2][2] + 10×5×60 = 1500 + 0 + 3000 = 4500. Dermed er dp[0][2] = 4500, oppnådd ved å multiplisere AB først.
Hvorfor denne fyllingsrekkefølgen fungerer
Når vi beregner dp[i][j], refererer vi til dp[i][k] og dp[k+1][j] for alle k i [i, j-1]. Begge delintervallene har strengt mindre lengde enn [i, j]. Ved å gå gjennom lengdene fra små til store er alle nødvendige delintervaller beregnet før vi trenger dem. Dette er det grunnleggende korrekthetsargumentet for fyllingsrekkefølgen i intervall-DP – kortere intervaller er alltid avhengigheter for lengre intervaller.
Memoisert top-down-intervall-DP
Intervall-DP kan også implementeres top-down med memoisering. Vi skriver en rekursiv funksjon solve(i, j) som returnerer den optimale kostnaden for intervallet [i, j], og mellomlagrer resultatene i en ordbok. Rekursjonen håndterer fyllingsrekkefølgen automatisk. Top-down er ofte enklere å resonnere over, men kan ha ekstra kostnad for funksjonskall; bottom-up er raskere i praksis for store inndata.
from functools import lru_cache
def matrix_chain_memo(dims):
n = len(dims) - 1
@lru_cache(maxsize=None)
def solve(i, j):
if i == j:
return 0
return min(
solve(i, k) + solve(k+1, j) + dims[i]*dims[k+1]*dims[j+1]
for k in range(i, j)
)
return solve(0, n-1)
print(matrix_chain_memo([10, 30, 5, 60])) # 4500Tids- og plasskompleksitet
Intervall-DP har O(n²) tilstander (alle par (i, j)), og hver tilstand går gjennom O(n) delingspunkter, noe som gir O(n³) tid totalt. Plassforbruket er O(n²) for DP-tabellen. For matrisekjedemultiplikasjon med 100 matriser tilsvarer dette 1 000 000 operasjoner – svært overkommelig. Mønsteret dukker opp i mange vanskelige LeetCode-problemer og er populært i FAANG-intervjuer på grunn av den lite åpenbare strukturen.
Gjenoppretting av den optimale løsningen
For å gjenopprette den faktiske parenteseringen (ikke bare kostnaden) lagrer du en separat split[i][j]-tabell som registrerer hvilken k som oppnådde minimumet for hver tilstand. Les deretter delingene rekursivt: reconstruct(i, j) skriver ut den optimale grupperingen ved å rekursere på [i, split[i][j]] og [split[i][j]+1, j]. Denne teknikken gjelder for alle intervall-DP-problemer.
def matrix_chain_with_split(dims):
n = len(dims) - 1
dp = [[0]*n for _ in range(n)]
split = [[0]*n for _ in range(n)]
for length in range(2, n + 1):
for i in range(n - length + 1):
j = i + length - 1
dp[i][j] = float('inf')
for k in range(i, j):
cost = dp[i][k] + dp[k+1][j] + dims[i]*dims[k+1]*dims[j+1]
if cost < dp[i][j]:
dp[i][j] = cost
split[i][j] = k
return dp[0][n-1], splitMal for alle intervall-DP-problemer
Den universelle malen for intervall-DP har tre deler: (1) initialiser basistilfellene for enkeltelementer, (2) gå gjennom økende lengder og, for hver lengde, gå gjennom gyldige venstregrenser mens du beregner høyregrensen, og (3) gå gjennom alle delingspunkter for hvert intervall og bruk den problemspesifikke rekurrensen. Det eneste som endres mellom problemene, er rekurrensformelen i den innerste løkken.
def interval_dp_template(n, base_cost, split_cost):
dp = [[float('inf')] * n for _ in range(n)]
for i in range(n):
dp[i][i] = base_cost(i) # problem-specific base case
for length in range(2, n + 1):
for i in range(n - length + 1):
j = i + length - 1
for k in range(i, j):
# problem-specific recurrence
candidate = dp[i][k] + dp[k+1][j] + split_cost(i, k, j)
dp[i][j] = min(dp[i][j], candidate)
return dp[0][n-1]Vanlige intervall-DP-problemer
Problemer som bruker intervall-DP, inkluderer: Matrix Chain Multiplication (minimer antall operasjoner), Burst Balloons (maksimer antall mynter), Strange Printer (minimer antall utskriftsoperasjoner), Minimum Score Triangulation of Polygon og Palindrome Partitioning II. Alle bruker det samme skjelettet for fyllingsrekkefølgen, men ulike rekurrenser. Gjenkjenn mønsteret når et problem ber om en optimal verdi over et område eller en sekvens som kan deles ved et vilkårlig indre punkt.
Hurtigsjekk
Test forståelsen din av konseptene i Data Structures & Algorithms — Coding Interview Prep fra denne leksjonen.
Oppsummering av leksjonen
I denne leksjonen lærte du at intervall-DP bruker dp[i][j] til å representere det optimale svaret over et område, at fyllingsrekkefølgen må følge økende intervallengde slik at delintervaller beregnes først, og at den universelle malen har O(n³) tid og O(n²) plass. Neste gang utforsker vi den lengste palindromiske delsekvensen og delstrengen ved hjelp av nettopp dette mønsteret.
Lær deg Forberedelse til kodeintervjuer med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 90
- Leksjoner
- 360
Ofte stilte spørsmål
Er leksjonen «Intervall-DP-mønster og utfyllingsrekkefølge» gratis?
Ja – hele teksten i «Intervall-DP-mønster og utfyllingsrekkefølge» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av Forberedelse til kodeintervjuer-kurset, kan du oppgradere til CoddyKit PRO. Kurset i Forberedelse til kodeintervjuer inneholder totalt 4 leksjoner.
Hva lærer jeg i «Intervall-DP-mønster og utfyllingsrekkefølge»?
Definer DP-tilstanden dp[i][j], forklar hvorfor intervaller må fylles ut i stigende lengderekkefølge, og følg mønsteret på matrise-kjedemultiplikasjon. Du øver på Forberedelse til kodeintervjuer med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med Forberedelse til kodeintervjuer?
Ingen tidligere erfaring er nødvendig. Forberedelse til kodeintervjuer på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 1 av 4.
Hvor lang tid tar leksjonen «Intervall-DP-mønster og utfyllingsrekkefølge»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne Forberedelse til kodeintervjuer-leksjonen?
Ja. Alle Forberedelse til kodeintervjuer-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- Intervall-DP-mønster og utfyllingsrekkefølge
- Lengste palindromiske delsekvens og delstreng
- Palindrom-partisjonering II
- Burst Balloons: Intervall-DP baklengs