Zurück zur Startseite

DP auf LeetCode: 3 Probleme mit Rezessionsformeln

Der Artikel zerlegt drei klassische dynamische Programmierungsprobleme von LeetCode: Pfadanzahlung, Minimierung der Pfadkosten und Treppensteigen. Er liefert Rezessionsrelationen, Python-Code und Anzeichen von DP-Problemen.

Dynamische Programmierung: Unique Paths + Min Path Sum
Advertisement 728x90

Dynamische Programmierung: Ein intuitiver Ansatz für drei LeetCode-Aufgaben

Dynamische Programmierung (DP) löst Zähl- und Optimierungsprobleme durch Rekursionsbeziehungen. Betrachten wir die Aufgaben 62 (Unique Paths), 64 (Minimum Path Sum) und 70 (Climbing Stairs). Jede zeigt Schlüsselmerkmale der DP: optimale Unterstruktur und überlappende Teilprobleme.

Unique Paths: Routen auf einem Gitter zählen

Ein Roboter bewegt sich auf einem m × n Gitter von (0,0) nach (m-1,n-1) und kann nur nach rechts oder unten gehen. Finde die Anzahl der eindeutigen Pfade.

Jeder Pfad besteht aus (m-1) Schritten nach unten und (n-1) Schritten nach rechts. Eine kombinatorische Lösung nutzt den Binomialkoeffizienten C(m+n-2, m-1). In Interviews wird jedoch ein algorithmischer Ansatz erwartet.

Google AdInline article slot

Definiere dp[i][j] als die Anzahl der Pfade zur Zelle (i,j). Rekursionsbeziehung:

dp[i][j] = dp[i-1][j] + dp[i][j-1]

Randbedingungen:

Google AdInline article slot
  • dp[0][j] = 1 (erste Zeile)
  • dp[i][0] = 1 (erste Spalte)
class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        dp = [[1] * n for _ in range(m)]
        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
        return dp[m - 1][n - 1]

Zeitkomplexität O(m·n). Die Lösung nutzt die Additionsregel für disjunkte Pfadmengen.

Minimum Path Sum: Minimierung der Pfadkosten

Gegeben ein m × n Gitter mit nicht-negativen Zahlen. Finde die minimale Summe der Werte auf einem Pfad von (0,0) nach (m-1,n-1) bei Bewegung nach rechts oder unten.

Ein Greedy-Algorithmus (lokales Minimum wählen) scheitert – er führt zu Sackgassen mit hohen Werten. Globale Optimierung ist erforderlich.

Google AdInline article slot

Definiere dp[i][j] als die minimalen Kosten, um (i,j) zu erreichen. Rekursionsbeziehung:

dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])

Randbedingungen:

dp[0][0] = grid[0][0]
for j in range(1, n): dp[0][j] = grid[0][j] + dp[0][j-1]
for i in range(1, m): dp[i][0] = grid[i][0] + dp[i-1][0]
class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:
        m, n = len(grid), len(grid[0])
        dp = [[0] * n for _ in range(m)]
        dp[0][0] = grid[0][0]
        for j in range(1, n):
            dp[0][j] = grid[0][j] + dp[0][j - 1]
        for i in range(1, m):
            dp[i][0] = grid[i][0] + dp[i - 1][0]
        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] = grid[i][j] + min(dp[i - 1][j], dp[i][j - 1])
        return dp[-1][-1]

Komplexität O(m·n). Jeder Wert wird für alle nachfolgenden Zellen verwendet.

Climbing Stairs: Fibonacci-Zahlen in der DP

Treppensteigen mit n Stufen (1 ≤ n ≤ 45), jede Stufe ist 1 oder 2 Stufen. Finde die Anzahl der Möglichkeiten.

Für n=1: 1 Möglichkeit

n=2: 2 Möglichkeiten (1+1, 2)

n=3: 3 Möglichkeiten (1+1+1, 1+2, 2+1)

Rekursionsbeziehung:

dp[n] = dp[n-1] + dp[n-2]

Einfache Rekursion führt zu exponentieller Komplexität O(φ^n) durch wiederholte Berechnungen. Ein DP-Array löst das Problem:

class Solution:
    def climbStairs(self, n: int) -> int:
        if n <= 2:
            return n
        dp = [0] * (n + 1)
        dp[1], dp[2] = 1, 2
        for i in range(3, n + 1):
            dp[i] = dp[i - 1] + dp[i - 2]
        return dp[n]

Eine optimierte Version nutzt O(1) Speicher mit zwei Variablen.

Anzeichen für Dynamische Programmierung Probleme

Wende DP an, wenn zwei Bedingungen erfüllt sind:

  • Optimale Unterstruktur: Die optimale Lösung besteht aus optimalen Lösungen für Teilprobleme.
  • Überlappende Teilprobleme: Dieselben Teilprobleme werden mehrfach berechnet.

| Problem | Optimale Unterstruktur | Überlappende Teilprobleme |

|--------|-------------------------|---------------------------|

| Unique Paths | Summe der Pfade von benachbarten Zellen | Jeder Pfad zu einer Zelle überlappt |

| Min Path Sum | Minimale Kosten von Nachbarn | Kosten werden für alle Zellen unten/rechts verwendet |

| Climbing Stairs | Summe von n-1 und n-2 | Rekursive Aufrufe wiederholen sich |

Wichtige Erkenntnisse

  • Rekursionsbeziehungen definieren das Wesen der DP: dp[i][j] hängt nur von bereits berechneten benachbarten Werten ab.
  • Randbedingungen werden separat für die erste Zeile/Spalte oder Basisfälle gefüllt.
  • Zeitkomplexität ist bei korrekter tabellarischer Implementierung immer O(Eingabegröße).
  • Speicher kann optimiert werden: für Gitter – eine Zeile, für lineare Probleme – zwei Variablen.
  • Kombinatorik bietet oft einen mathematischen Abkürzung, aber algorithmische Lösungen sind für Interviews wichtiger.

— Editorial Team

Advertisement 728x90

Weiterlesen