Bölüm 6: Dinamik Programlama

Algoritmalar
Örtüşen alt problemlerMemoization ve tablolamaFibonacci0/1 ve sınırsız sırt çantası Alt küme toplamıEn uzun artan alt diziEn uzun ortak alt dizgiEn uzun ortak alt dizi
Sercan KÜLCÜ · Giresun Üniversitesi · Bilgisayar Mühendisliği

Giriş

Öğrenme Çıktıları

Tanıma

Bir problemde örtüşen alt problemleri ve optimal alt yapıyı fark etmek; dinamik programlamanın ne zaman uygun olduğuna karar vermek.

Formülleme

Alt problemi (durumu) tanımlamak, özyineleme bağıntısını ve taban durumlarını yazmak, tablo doldurma sırasını belirlemek.

Uygulama

Fibonacci, 0/1 ve sınırsız sırt çantası, alt küme toplamı, LIS, en uzun ortak alt dizgi ve LCS tablolarını elle doldurmak ve Java ile yazmak.

Analiz

Memoization ile tablolamayı karşılaştırmak; O(n · W), O(m · n), O(n²), O(n log n) karmaşıklıklarını gerekçelendirmek; tablodan çözümü geri izlemek.

Temel Kavramlar

Dinamik Programlama (Dynamic Programming) Nedir?

  • Bazen karmaşık problemleri çözmek için akıllı yöntemlere ihtiyaç olur; dinamik programlama bunlardan biridir.
  • Büyük problemi küçük, tekrarlayan alt problemlere böler ve adım adım çözer.
  • Her alt problemin çözümü bir tabloya yazılır; aynı alt problemle yeniden karşılaşıldığında çözüm tablodan okunur, böylece zaman kazanılır.
  • Özellikle optimizasyon problemlerinde (en büyük değer, en kısa yol, en az işlem) sıkça kullanılır.
Kısaca

DP = özyineleme + hatırlama. Her dinamik programlama problemi düz özyineleme ile de çözülebilir; DP, aynı işi iki kez yapmamayı sağlar.

Ad, Richard Bellman'dan (1950'ler) gelir; "programlama" burada kod yazmak değil, tablo ile planlama anlamındadır.

Temel Kavramlar

DP İçin İki Temel Özellik

1. Örtüşen alt problemler (overlapping subproblems)

Özyinelemeli çözüm aynı alt problemi tekrar tekrar çağırır. Örnek: fib(5) hesaplanırken fib(3) iki kez, fib(2) üç kez hesaplanır.

Alt problemler birbirinden bağımsızsa (birleştirme sıralaması gibi) tablo işe yaramaz; bu böl ve fethettir.

2. Optimal alt yapı (optimal substructure)

Problemin optimal çözümü, alt problemlerin optimal çözümlerinden kurulabilir.

Bellman'ın optimallik ilkesi: optimal bir çözümün içerdiği her alt çözüm de kendi alt problemi için optimaldir.

Bellman denklemi örneği (en büyük toplamlı alt dizi): V(i) = i'de biten en büyük toplam ise V(i) = max(a[i], V(i−1) + a[i]). Değer, bir önceki alt problemin optimal değerinden hesaplanır.

Temel Kavramlar

Bir DP Çözümü Tasarlamanın Adımları

  1. Optimal çözümün yapısını anla: hangi alt problemler çözülmeli?
  2. Durumu ve bağıntıyı yaz: çözümü daha küçük alt problemler cinsinden ifade et (özyineleme bağıntısı + taban durumları).
  3. Hesaplama sırasını belirle: bir hücre hesaplanırken ihtiyaç duyduğu hücreler hazır olmalı.
  4. Tablonun boyutunu belirle: kaç boyut, her boyut kaç değer? (ör. (n+1) × (W+1))
  5. Tabloyu doldur: aşağıdan yukarı (tablolama) ya da yukarıdan aşağı (memoization).
  6. Çözümü geri izle: yalnızca değeri değil, seçilen elemanları da tablodan geriye doğru okuyarak bul.

Toplam süre ≈ (durum sayısı) × (bir durumun hesaplanma süresi). Bu kural bölümdeki tüm karmaşıklıkları açıklar.

Temel Kavramlar

Memoization ve Tablolama (Tabulation)

Yukarıdan aşağı: Memoization
  • Özyinelemeli çözüm aynen yazılır; sonuçlar bir önbellekte (dizi / HashMap) saklanır.
  • Çağrıdan önce tabloya bakılır: varsa döndürülür, yoksa hesaplanıp yazılır.
  • Yalnızca gerçekten ihtiyaç duyulan alt problemler çözülür.
  • Derin özyinelemede yığın taşması (StackOverflowError) riski.
Aşağıdan yukarı: Tablolama
  • En küçük alt problemden başlanır, tablo döngülerle sırayla doldurulur.
  • Özyineleme yok, çağrı maliyeti yok; genellikle daha hızlıdır.
  • Doldurma sırası önceden doğru belirlenmelidir.
  • Yalnızca önceki satır gerekiyorsa bellek azaltılabilir (ör. O(W)).
İkisi de her alt problemi bir kez çözer; asimptotik süre aynıdır. Fark, sıra ve uygulama biçimindedir.

Temel Kavramlar

DP, Böl ve Fethet ve Açgözlü Yaklaşım

Böl ve FethetDinamik ProgramlamaAçgözlü (Greedy)
Alt problemlerbağımsızörtüşenher adımda tek seçim
Seçimyok, böl ve birleştirtüm seçenekler denenir, en iyisi tabloyao anki en iyi seçilir, geri dönülmez
Optimallik—optimal alt yapı varsa garantiyalnızca açgözlü seçim özelliği varsa
Örnekbirleştirme sıralaması, ikili arama0/1 sırt çantası, LCS, Bellman-Fordkesirli sırt çantası, Dijkstra, Kruskal
Belleközyineleme yığınıtablo (çoğu zaman O(n · W) ya da O(m · n))genellikle O(1) ek bellek

Temel Kavramlar

Dinamik Programlama Uygulamaları

En kısa yolGPS cihazları, haritalar: Bellman-Ford, Floyd-Warshall.
Dizgi karşılaştırmadiff, yazım denetimi: LCS, düzenleme uzaklığı (edit distance).
BiyoinformatikDNA/protein hizalama (Needleman-Wunsch, Smith-Waterman), RNA yapısının belirlenmesi.
Kaynak dağıtımıSırt çantası, bütçe ve yatırım planlama, üretim hattı çizelgeleme.
Derleyiciler ve sayısal hesapMatris zinciri çarpımı, optimal ikili arama ağacı, ayrıştırma (CYK).
Yapay zekâSatranç oynayan bilgisayarlar, makine çevirisi, pekiştirmeli öğrenme (Bellman denklemi).

Fibonacci

Fibonacci: Düz Özyineleme

Fibonacci dizisi: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …

F(0) = 0, F(1) = 1
F(n) = F(n−1) + F(n−2), n ≥ 2
static long fib(int n) {
  if (n <= 1)
    return n;         // taban durumları
  return fib(n - 1) + fib(n - 2);
}
  • Tanımın birebir kopyası; doğru ama çok yavaş.
  • Çağrı sayısı: T(n) = T(n−1) + T(n−2) + 1 → Θ(φⁿ), φ ≈ 1,618 (altın oran).
  • fib(40) yaklaşık 331 milyon çağrı yapar; fib(50) ise dakikalar sürer.
  • Neden? Aynı alt problemler (fib(3), fib(2), …) tekrar tekrar hesaplanır: örtüşen alt problemler.

Fibonacci

fib(5) Özyineleme Ağacı: Çağrı Sırası

Turuncu: çalışan çağrı · mavi: yığında bekleyen çağrılar · yeşil: bitmiş çağrı (değeriyle).

f(5)f(4)f(3)f(2)f(1)f(0)f(1)f(2)f(1)f(0)f(3)f(2)f(1)f(0)f(1)

fib(5) çağrıldı Yığın: fib(5)

f(5)f(4)f(3)f(2)f(1)f(0)f(1)f(2)f(1)f(0)f(3)f(2)f(1)f(0)f(1)

fib(4) çağrıldı Yığın: fib(5) → fib(4)

f(5)f(4)f(3)f(2)f(1)f(0)f(1)f(2)f(1)f(0)f(3)f(2)f(1)f(0)f(1)

fib(3) çağrıldı Yığın: fib(5) → fib(4) → fib(3)

f(5)f(4)f(3)f(2)f(1)f(0)f(1)f(2)f(1)f(0)f(3)f(2)f(1)f(0)f(1)

fib(2) çağrıldı Yığın: fib(5) → fib(4) → fib(3) → fib(2)

f(5)f(4)f(3)f(2)f(1)f(0)f(1)f(2)f(1)f(0)f(3)f(2)f(1)f(0)f(1)

fib(1) taban durumu: hemen 1 döner. Yığın: fib(5) → fib(4) → fib(3) → fib(2) → fib(1)

f(5)f(4)f(3)f(2)f(1)=1f(0)f(1)f(2)f(1)f(0)f(3)f(2)f(1)f(0)f(1)

fib(0) taban durumu: hemen 0 döner. Yığın: fib(5) → fib(4) → fib(3) → fib(2) → fib(0)

f(5)f(4)f(3)f(2)=1f(1)=1f(0)=0f(1)f(2)f(1)f(0)f(3)f(2)f(1)f(0)f(1)

fib(1) taban durumu: hemen 1 döner. Yığın: fib(5) → fib(4) → fib(3) → fib(1)

f(5)f(4)f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1f(2)f(1)f(0)f(3)f(2)f(1)f(0)f(1)

fib(2) çağrıldı — 2. kez hesaplanıyor! Yığın: fib(5) → fib(4) → fib(2)

f(5)f(4)f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1f(2)f(1)f(0)f(3)f(2)f(1)f(0)f(1)

fib(1) taban durumu: hemen 1 döner. Yığın: fib(5) → fib(4) → fib(2) → fib(1)

f(5)f(4)f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1f(2)f(1)=1f(0)f(3)f(2)f(1)f(0)f(1)

fib(0) taban durumu: hemen 0 döner. Yığın: fib(5) → fib(4) → fib(2) → fib(0)

f(5)f(4)=3f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1f(2)=1f(1)=1f(0)=0f(3)f(2)f(1)f(0)f(1)

fib(3) çağrıldı — 2. kez hesaplanıyor! Yığın: fib(5) → fib(3)

f(5)f(4)=3f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1f(2)=1f(1)=1f(0)=0f(3)f(2)f(1)f(0)f(1)

fib(2) çağrıldı — 3. kez hesaplanıyor! Yığın: fib(5) → fib(3) → fib(2)

f(5)f(4)=3f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1f(2)=1f(1)=1f(0)=0f(3)f(2)f(1)f(0)f(1)

fib(1) taban durumu: hemen 1 döner. Yığın: fib(5) → fib(3) → fib(2) → fib(1)

f(5)f(4)=3f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1f(2)=1f(1)=1f(0)=0f(3)f(2)f(1)=1f(0)f(1)

fib(0) taban durumu: hemen 0 döner. Yığın: fib(5) → fib(3) → fib(2) → fib(0)

f(5)f(4)=3f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1f(2)=1f(1)=1f(0)=0f(3)f(2)=1f(1)=1f(0)=0f(1)

fib(1) taban durumu: hemen 1 döner. Yığın: fib(5) → fib(3) → fib(1)

f(5)=5f(4)=3f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1f(2)=1f(1)=1f(0)=0f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1

Sonuç fib(5) = 5, toplam 15 çağrı. Kırmızı kutular tekrar: fib(3) 2 kez, fib(2) 3 kez, fib(1) 5 kez hesaplandı.

Fibonacci

Tekrarlanan Hesapların Bedeli

nF(n)düz özyineleme çağrısımemoization çağrısı
55159
105517719
206.76521.89139
30832.0402.692.53759
40102.334.155331.160.28179
  • Düz özyinelemede çağrı sayısı 2·F(n+1) − 1: Fibonacci sayılarının kendisi kadar hızlı (üstel) büyür.
  • Oysa yalnızca n+1 farklı alt problem vardır: fib(0), fib(1), …, fib(n).
  • Her birini bir kez çözüp saklarsak çağrı sayısı 2n − 1'e iner: üstelden doğrusala.
DP'nin özü: farklı alt problem sayısı küçük, ama özyinelemenin onları çağırma sayısı büyükse, sonuçları saklamak büyük kazanç sağlar.

Fibonacci

Memoization: fib(5) Adım Adım

f(5)bellek—0—102030405

fib(5): tabloda yok; önce fib(4) çağrılır.

f(5)f(4)bellek—0—102030405

fib(4): tabloda yok; önce fib(3) çağrılır.

f(5)f(4)f(3)bellek—0—102030405

fib(3): tabloda yok; önce fib(2) çağrılır.

f(5)f(4)f(3)f(2)bellek—0—102030405

fib(2): tabloda yok; önce fib(1) çağrılır.

f(5)f(4)f(3)f(2)f(1)=1bellek—0—102030405

fib(1): taban durumu, 1 döner.

f(5)f(4)f(3)f(2)f(1)=1bellek—0—102030405

fib(1) döndü (a = 1); şimdi fib(0) çağrılır.

f(5)f(4)f(3)f(2)f(1)=1f(0)=0bellek—0—102030405

fib(0): taban durumu, 0 döner.

f(5)f(4)f(3)f(2)=1f(1)=1f(0)=0bellek—0—112030405

bellek[2] = 1 + 0 = 1 tabloya yazılır ve döndürülür.

f(5)f(4)f(3)f(2)=1f(1)=1f(0)=0bellek—0—112030405

fib(2) döndü (a = 1); şimdi fib(1) çağrılır.

f(5)f(4)f(3)f(2)=1f(1)=1f(0)=0f(1)=1bellek—0—112030405

fib(1): taban durumu, 1 döner.

f(5)f(4)f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1bellek—0—112230405

bellek[3] = 1 + 1 = 2 tabloya yazılır ve döndürülür.

f(5)f(4)f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1bellek—0—112230405

fib(3) döndü (a = 2); şimdi fib(2) çağrılır.

f(5)f(4)f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1f(2)=1bellek—0—112230405

fib(2): bellek[2] = 1 tabloda hazır; yeniden hesaplanmaz.

f(5)f(4)=3f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1f(2)=1bellek—0—112233405

bellek[4] = 2 + 1 = 3 tabloya yazılır ve döndürülür.

f(5)f(4)=3f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1f(2)=1bellek—0—112233405

fib(4) döndü (a = 3); şimdi fib(3) çağrılır.

f(5)f(4)=3f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1f(2)=1f(3)=2bellek—0—112233405

fib(3): bellek[3] = 2 tabloda hazır; yeniden hesaplanmaz.

f(5)=5f(4)=3f(3)=2f(2)=1f(1)=1f(0)=0f(1)=1f(2)=1f(3)=2bellek—0—112233455

bellek[5] = 3 + 2 = 5 tabloya yazılır ve döndürülür.

static long[] bellek = new long[50];

static long fib(int n) {
  if (n <= 1) return n;          // taban
  if (bellek[n] != 0) return bellek[n];
  long a = fib(n - 1);
  long b = fib(n - 2);
  bellek[n] = a + b;
  return bellek[n];
}

Fibonacci

Tablolama: fibonacci(9) Adım Adım

00010203040506070809

fib dizisi n + 1 = 10 elemanla oluşturulur (Java'da başlangıçta hepsi 0).

001123456789

Taban durumları yazılır: fib[0] = 0, fib[1] = 1. Boş hücreler henüz hesaplanmadı.

0011123456789+

i = 2: fib[2] = fib[1] + fib[0] = 1 + 0 = 1

00111223456789+

i = 3: fib[3] = fib[2] + fib[1] = 1 + 1 = 2

001112233456789+

i = 4: fib[4] = fib[3] + fib[2] = 2 + 1 = 3

0011122334556789+

i = 5: fib[5] = fib[4] + fib[3] = 3 + 2 = 5

00111223345586789+

i = 6: fib[6] = fib[5] + fib[4] = 5 + 3 = 8

0011122334558613789+

i = 7: fib[7] = fib[6] + fib[5] = 8 + 5 = 13

001112233455861372189+

i = 8: fib[8] = fib[7] + fib[6] = 13 + 8 = 21

00111223345586137218349+

i = 9: fib[9] = fib[8] + fib[7] = 21 + 13 = 34

00111223345586137218349

Döngü biter; fib[9] = 34 döndürülür. Her hücre bir kez hesaplandı: O(n) zaman, O(n) bellek.

static int fibonacci(int n) {
  int[] fib = new int[n + 1];
  fib[0] = 0;   // taban durumları
  fib[1] = 1;
  for (int i = 2; i <= n; i++)
    fib[i] = fib[i - 1] + fib[i - 2];
  return fib[n];
}

Fibonacci

Fibonacci: O(1) Bellek ve Karşılaştırma

static long fib(int n) {
  if (n <= 1) return n;
  long once = 0, simdi = 1;
  for (int i = 2; i <= n; i++) {
    long yeni = once + simdi;
    once = simdi;
    simdi = yeni;
  }
  return simdi;
}
YöntemZamanEk bellek
Düz özyinelemeΘ(φⁿ)O(n) yığın
MemoizationO(n)O(n) tablo + yığın
TablolamaO(n)O(n) tablo
İki değişkenO(n)O(1)
Bellek azaltma: fib[i] yalnızca son iki değere bağlıdır; tüm diziyi saklamaya gerek yoktur. Aynı fikir sırt çantasında "yalnızca önceki satır" olarak karşımıza çıkacak.

0/1 Sırt Çantası

0/1 Sırt Çantası (0/1 Knapsack) Problemi

  • n eşya: her eşyanın bir değeri vᵢ ve bir ağırlığı (hacmi) wᵢ var.
  • Çantanın kapasitesi W.
  • Her eşya ya tamamen alınır (1) ya da hiç alınmaz (0); bölünemez.
  • Amaç: toplam ağırlık ≤ W olacak şekilde toplam değeri en büyük yapmak.
15 kgkapasite4 ₺12 kg2 ₺2 kg2 ₺1 kg1 ₺1 kg10 ₺4 kg
Soru: Resimdeki çantaya hangi eşyalar konmalı?
Cevap12 kg'lık eşya dışındaki dördü: 2 + 2 + 1 + 10 = 15 ₺, toplam 8 kg. 12 kg'lık eşyayı alan her seçim en fazla 4 + 2 + 2 = 8 ₺ eder.

0/1 Sırt Çantası

Neden Kaba Kuvvet ya da Açgözlü Değil?

Kaba kuvvet

Her eşya için 2 seçenek → 2ⁿ alt küme. n = 50 için 10¹⁵'ten fazla küme: pratikte imkânsız.

Açgözlü (değer/ağırlık oranı)

W = 50; eşyalar (60 ₺, 10 kg), (100 ₺, 20 kg), (120 ₺, 30 kg). Oranlar 6, 5, 4 → açgözlü ilk ikisini alır: 160 ₺. Optimal: ikinci + üçüncü = 220 ₺.

Açgözlü seçim yalnızca eşyalar bölünebiliyorsa (kesirli sırt çantası) optimaldir. 0/1 durumunda bir eşyayı almak, sonraki seçenekleri değiştirir: her iki seçeneği de denemek gerekir → dinamik programlama.

0/1 Sırt Çantası

0/1 Sırt Çantası: Durum ve Bağıntı

Durum: K[i][c] = ilk i eşya ve c kapasite ile elde edilebilecek en büyük değer.

K[0][c] = 0, K[i][0] = 0
wᵢ > c ise: K[i][c] = K[i−1][c]
aksi hâlde: K[i][c] = max(K[i−1][c], vᵢ + K[i−1][c − wᵢ])
  • Almazsam: kapasite aynı kalır, önceki eşyalarla en iyisi.
  • Alırsam: vᵢ kazanırım, kalan c − wᵢ kapasiteyi önceki eşyalarla en iyi şekilde doldururum.
  • Cevap: K[n][W]. Tablo (n+1) × (W+1), satır satır doldurulur.
K[i−1][c−wᵢ]K[i−1][c]K[i][c]almazsamalırsam: + vᵢsatır i−1 (önceki eşyalar)satır i

0/1 Sırt Çantası

0/1 Sırt Çantası: Tabloyu Adım Adım Doldur (W = 6)

c (kapasite) →ivw0123456000000001540243033204210

Tablo (n+1) × (W+1) = 5 × 7. Satır 0 (eşya yok) ve sütun 0 (kapasite yok) 0'dır.

c (kapasite) →ivw01234560000000015400243033204210

i=1, c=1: w=4 > 1, eşya sığmaz → üstteki değer kopyalanır: K[1][1] = 0

c (kapasite) →ivw012345600000000154000243033204210

i=1, c=2: w=4 > 2, eşya sığmaz → üstteki değer kopyalanır: K[1][2] = 0

c (kapasite) →ivw0123456000000001540000243033204210

i=1, c=3: w=4 > 3, eşya sığmaz → üstteki değer kopyalanır: K[1][3] = 0

c (kapasite) →ivw01234560000000015400005243033204210

i=1, c=4: max(alma = 0, al = 5 + K[0][0] = 5) = 5 (al)

c (kapasite) →ivw012345600000000154000055243033204210

i=1, c=5: max(alma = 0, al = 5 + K[0][1] = 5) = 5 (al)

c (kapasite) →ivw0123456000000001540000555243033204210

i=1, c=6: max(alma = 0, al = 5 + K[0][2] = 5) = 5 (al)

c (kapasite) →ivw01234560000000015400005552430033204210

i=2, c=1: w=3 > 1, eşya sığmaz → üstteki değer kopyalanır: K[2][1] = 0

c (kapasite) →ivw012345600000000154000055524300033204210

i=2, c=2: w=3 > 2, eşya sığmaz → üstteki değer kopyalanır: K[2][2] = 0

c (kapasite) →ivw0123456000000001540000555243000433204210

i=2, c=3: max(alma = 0, al = 4 + K[1][0] = 4) = 4 (al)

c (kapasite) →ivw01234560000000015400005552430004533204210

i=2, c=4: max(alma = 5, al = 4 + K[1][1] = 4) = 5 (alma)

c (kapasite) →ivw012345600000000154000055524300045533204210

i=2, c=5: max(alma = 5, al = 4 + K[1][2] = 4) = 5 (alma)

c (kapasite) →ivw0123456000000001540000555243000455533204210

i=2, c=6: max(alma = 5, al = 4 + K[1][3] = 4) = 5 (alma)

c (kapasite) →ivw01234560000000015400005552430004555332004210

i=3, c=1: w=2 > 1, eşya sığmaz → üstteki değer kopyalanır: K[3][1] = 0

c (kapasite) →ivw012345600000000154000055524300045553320034210

i=3, c=2: max(alma = 0, al = 3 + K[2][0] = 3) = 3 (al)

c (kapasite) →ivw0123456000000001540000555243000455533200344210

i=3, c=3: max(alma = 4, al = 3 + K[2][1] = 3) = 4 (alma)

c (kapasite) →ivw01234560000000015400005552430004555332003454210

i=3, c=4: max(alma = 5, al = 3 + K[2][2] = 3) = 5 (alma)

c (kapasite) →ivw012345600000000154000055524300045553320034574210

i=3, c=5: max(alma = 5, al = 3 + K[2][3] = 7) = 7 (al)

c (kapasite) →ivw0123456000000001540000555243000455533200345784210

i=3, c=6: max(alma = 5, al = 3 + K[2][4] = 8) = 8 (al)

c (kapasite) →ivw01234560000000015400005552430004555332003457842102

i=4, c=1: max(alma = 0, al = 2 + K[3][0] = 2) = 2 (al)

c (kapasite) →ivw012345600000000154000055524300045553320034578421023

i=4, c=2: max(alma = 3, al = 2 + K[3][1] = 2) = 3 (alma)

c (kapasite) →ivw0123456000000001540000555243000455533200345784210235

i=4, c=3: max(alma = 4, al = 2 + K[3][2] = 5) = 5 (al)

c (kapasite) →ivw01234560000000015400005552430004555332003457842102356

i=4, c=4: max(alma = 5, al = 2 + K[3][3] = 6) = 6 (al)

c (kapasite) →ivw012345600000000154000055524300045553320034578421023567

i=4, c=5: max(alma = 7, al = 2 + K[3][4] = 7) = 7 (alma)

c (kapasite) →ivw0123456000000001540000555243000455533200345784210235679

i=4, c=6: max(alma = 8, al = 2 + K[3][5] = 9) = 9 (al)

c (kapasite) →ivw0123456000000001540000555243000455533200345784210235679

Tablo doldu. Cevap K[4][6] = 9. Toplam 4 × 6 = 24 hücre, her biri O(1).

int sirtCantasi(int[] v, int[] w, int W) {
  int n = v.length;
  int[][] K = new int[n + 1][W + 1];
  for (int i = 1; i <= n; i++)
    for (int c = 1; c <= W; c++) {
      int vi = v[i - 1], wi = w[i - 1];
      if (wi > c)            // sığmaz
        K[i][c] = K[i - 1][c];
      else                   // alma / al
        K[i][c] = Math.max(K[i - 1][c],
                    vi + K[i - 1][c - wi]);
    }
  return K[n][W];
}

0/1 Sırt Çantası

Geri İzleme: Hangi Eşyalar Alındı?

Kural: K[i][c] ≠ K[i−1][c] ise eşya i alınmıştır (değer ancak onu alarak arttı) → c ← c − wᵢ; eşitse alınmamıştır.

c (kapasite) →ivw0123456000000001540000555243000455533200345784210235679

Geri izleme K[4][6] = 9 hücresinden başlar. Hangi eşyalar alındı?

c (kapasite) →ivw0123456000000001540000555243000455533200345784210235679

K[4][6] = 9 ≠ K[3][6] = 8 → eşya 4 alındı (v=2, w=1); c = 6 − 1 = 5

c (kapasite) →ivw0123456000000001540000555243000455533200345784210235679

K[3][5] = 7 ≠ K[2][5] = 5 → eşya 3 alındı (v=3, w=2); c = 5 − 2 = 3

c (kapasite) →ivw0123456000000001540000555243000455533200345784210235679

K[2][3] = 4 ≠ K[1][3] = 0 → eşya 2 alındı (v=4, w=3); c = 3 − 3 = 0

c (kapasite) →ivw0123456000000001540000555243000455533200345784210235679

K[1][0] = K[0][0] = 0 → eşya 1 alınmadı; c değişmez.

c (kapasite) →ivw0123456000000001540000555243000455533200345784210235679

Seçilen eşyalar: 2, 3, 4 → değer 4 + 3 + 2 = 9, ağırlık 3 + 2 + 1 = 6 = W.

0/1 Sırt Çantası

0/1 Sırt Çantası: Karmaşıklık ve Bellek

  • Zaman: O(n · W), bellek: O(n · W) — (n+1)(W+1) hücre, her biri O(1).
  • Bu süre sözde-polinomdur (pseudo-polynomial): W'nin değerine bağlıdır, bit uzunluğuna değil. W = 10⁹ ise tablo kurulamaz.
  • 0/1 sırt çantası NP-zordur; DP yalnızca W küçükken pratiktir.
  • Bellek azaltma (sağdaki kod): satır i yalnızca satır i−1'i kullanır → tek boyutlu dizi, O(W). Kapasite büyükten küçüğe gezilmeli; yoksa aynı eşya iki kez alınır.
  • Tek boyutlu sürümde geri izleme için ayrıca boolean[][] dahil tablosu tutulur.
int[] dp = new int[W + 1];
for (int i = 0; i < n; i++)
  for (int c = W; c >= w[i]; c--)
    dp[c] = Math.max(dp[c],
                     v[i] + dp[c - w[i]]);
return dp[W];

Sınırsız Sırt Çantası

Sınırsız Sırt Çantası (Unbounded Knapsack)

  • Her eşyadan istenildiği kadar (sınırsız sayıda) alınabilir; gerisi 0/1 ile aynıdır.
  • Durum: dp[i][c] = 0..i eşya türleri ve c kapasite ile en büyük kâr.
dp[i][c] = max( dp[i−1][c] , kârᵢ + dp[i][c − ağırlıkᵢ] ) (ağırlıkᵢ ≤ c ise)
0/1 ile tek fark"Alırsam" durumunda aynı satıra (i) bakılır, üst satıra (i−1) değil: eşyayı aldıktan sonra yine alabilirim.
ÖrnekKâr = [15, 50, 60, 90], ağırlık = [1, 3, 4, 5], kapasite = 8. Tablo 4 × 9; ilk satırda yalnızca eşya 0 (15 kâr, 1 kg) kullanılabilir.

Satır indisi 0'dan başlar (dp[−1][c] = 0 kabul edilir).

Sınırsız Sırt Çantası

Sınırsız Sırt Çantası: Tabloyu Adım Adım Doldur

kapasite →kârağır.i01234567815100503106042090530

Kapasite 0 sütunu 0'dır: hiçbir eşya sığmaz.

kapasite →kârağır.i0123456781510015503106042090530

i=0, c=1: yalnızca eşya 0 var, ağırlığı 1 sığar → 15 + dp[0][0] = 15.

kapasite →kârağır.i01234567815100153045607590105120503106042090530

i=0, c=2..8: dp[0][c] = 15 + dp[0][c−1] → eşya 0'dan c tane: 30, 45, …, 120. Aynı satır tekrar kullanıldı!

kapasite →kârağır.i0123456781510015304560759010512050310156042090530

i=1, c=1: ağırlık 3 > 1, sığmaz → dp[1][1] = dp[0][1] = 15

kapasite →kârağır.i012345678151001530456075901051205031015306042090530

i=1, c=2: ağırlık 3 > 2, sığmaz → dp[1][2] = dp[0][2] = 30

kapasite →kârağır.i01234567815100153045607590105120503101530506042090530

i=1, c=3: max(dp[0][3] = 45, 50 + dp[1][0] = 50) = 50

kapasite →kârağır.i0123456781510015304560759010512050310153050656042090530

i=1, c=4: max(dp[0][4] = 60, 50 + dp[1][1] = 65) = 65

kapasite →kârağır.i012345678151001530456075901051205031015305065806042090530

i=1, c=5: max(dp[0][5] = 75, 50 + dp[1][2] = 80) = 80

kapasite →kârağır.i012345678151001530456075901051205031015305065801006042090530

i=1, c=6: max(dp[0][6] = 90, 50 + dp[1][3] = 100) = 100

kapasite →kârağır.i012345678151001530456075901051205031015305065801001156042090530

i=1, c=7: max(dp[0][7] = 105, 50 + dp[1][4] = 115) = 115

kapasite →kârağır.i012345678151001530456075901051205031015305065801001151306042090530

i=1, c=8: max(dp[0][8] = 120, 50 + dp[1][5] = 130) = 130

kapasite →kârağır.i01234567815100153045607590105120503101530506580100115130604201590530

i=2, c=1: ağırlık 4 > 1, sığmaz → dp[2][1] = dp[1][1] = 15

kapasite →kârağır.i0123456781510015304560759010512050310153050658010011513060420153090530

i=2, c=2: ağırlık 4 > 2, sığmaz → dp[2][2] = dp[1][2] = 30

kapasite →kârağır.i012345678151001530456075901051205031015305065801001151306042015305090530

i=2, c=3: ağırlık 4 > 3, sığmaz → dp[2][3] = dp[1][3] = 50

kapasite →kârağır.i01234567815100153045607590105120503101530506580100115130604201530506590530

i=2, c=4: max(dp[1][4] = 65, 60 + dp[2][0] = 60) = 65

kapasite →kârağır.i0123456781510015304560759010512050310153050658010011513060420153050658090530

i=2, c=5: max(dp[1][5] = 80, 60 + dp[2][1] = 75) = 80

kapasite →kârağır.i0123456781510015304560759010512050310153050658010011513060420153050658010090530

i=2, c=6: max(dp[1][6] = 100, 60 + dp[2][2] = 90) = 100

kapasite →kârağır.i0123456781510015304560759010512050310153050658010011513060420153050658010011590530

i=2, c=7: max(dp[1][7] = 115, 60 + dp[2][3] = 110) = 115

kapasite →kârağır.i0123456781510015304560759010512050310153050658010011513060420153050658010011513090530

i=2, c=8: max(dp[1][8] = 130, 60 + dp[2][4] = 125) = 130

kapasite →kârağır.i012345678151001530456075901051205031015305065801001151306042015305065801001151309053015

i=3, c=1: ağırlık 5 > 1, sığmaz → dp[3][1] = dp[2][1] = 15

kapasite →kârağır.i01234567815100153045607590105120503101530506580100115130604201530506580100115130905301530

i=3, c=2: ağırlık 5 > 2, sığmaz → dp[3][2] = dp[2][2] = 30

kapasite →kârağır.i0123456781510015304560759010512050310153050658010011513060420153050658010011513090530153050

i=3, c=3: ağırlık 5 > 3, sığmaz → dp[3][3] = dp[2][3] = 50

kapasite →kârağır.i012345678151001530456075901051205031015305065801001151306042015305065801001151309053015305065

i=3, c=4: ağırlık 5 > 4, sığmaz → dp[3][4] = dp[2][4] = 65

kapasite →kârağır.i01234567815100153045607590105120503101530506580100115130604201530506580100115130905301530506590

i=3, c=5: max(dp[2][5] = 80, 90 + dp[3][0] = 90) = 90

kapasite →kârağır.i01234567815100153045607590105120503101530506580100115130604201530506580100115130905301530506590105

i=3, c=6: max(dp[2][6] = 100, 90 + dp[3][1] = 105) = 105

kapasite →kârağır.i01234567815100153045607590105120503101530506580100115130604201530506580100115130905301530506590105120

i=3, c=7: max(dp[2][7] = 115, 90 + dp[3][2] = 120) = 120

kapasite →kârağır.i01234567815100153045607590105120503101530506580100115130604201530506580100115130905301530506590105120140

i=3, c=8: max(dp[2][8] = 130, 90 + dp[3][3] = 140) = 140

kapasite →kârağır.i01234567815100153045607590105120503101530506580100115130604201530506580100115130905301530506590105120140

Cevap dp[3][8] = 140: 90 + dp[3][3] = 90 + 50 → bir tane eşya 3 (90, 5 kg) + bir tane eşya 1 (50, 3 kg).

Sınırsız Sırt Çantası

Sınırsız Sırt Çantası: Tek Boyutlu Çözüm

static int sinirsizSirtCantasi(int[] w, int[] v, int W) {
  int[] dp = new int[W + 1];       // dp[c]: c kapasiteyle en iyi
  for (int c = 1; c <= W; c++)
    for (int j = 0; j < w.length; j++)
      if (w[j] <= c)
        dp[c] = Math.max(dp[c], dp[c - w[j]] + v[j]);
  return dp[W];
}
  • Satırlar birleştirilebilir: dp[c] = tüm eşyalar için max(dp[c − wⱼ] + vⱼ).
  • Örnek için dp = [0, 15, 30, 50, 65, 90, 105, 120, 140] — tablonun son satırı.
  • Zaman O(n · W), bellek O(W).
Döngü yönü her şeyi belirlerTek boyutlu dizide kapasite küçükten büyüğe gezilirse dp[c − w] bu tur güncellenmiş olur → eşya tekrar alınabilir (sınırsız). Büyükten küçüğe gezilirse eski değer okunur → 0/1.

Alt Küme Toplamı

Alt Küme Toplamı (Subset Sum)

1010005588662244hedef toplam = 155 + 8 + 2 = 15✓✓✓
  • Verilen tam sayılardan, toplamı tam olarak hedef değere eşit olan bir alt küme var mı? (Karar problemi: evet / hayır.)
  • Durum: dp[i][j] = ilk i elemanla toplam j elde edilebilir mi? (boolean)
dp[i][0] = doğru · dp[0][j] = yanlış (j > 0)
dp[i][j] = dp[i−1][j] veya ( aᵢ ≤ j ve dp[i−1][j − aᵢ] )

Sırt çantasının "değer = ağırlık" özel durumudur: max yerine mantıksal veya kullanılır.

Alt Küme Toplamı

Alt Küme Toplamı: Tablo (a = 10, 0, 5, 8, 6, 2, 4; hedef 15)

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓20✓35✓48✓56✓62✓74✓

Sütun 0 hep ✓ (boş küme toplamı 0). Satır 0'da (eleman yok) diğer toplamlar elde edilemez (·).

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓·········✓·····20✓35✓48✓56✓62✓74✓

i=1, a=10: satır i−1 kopyalanır, ayrıca dp[i−1][j−10] ✓ olan her j için ✓ → yeni ulaşılan toplamlar: 10.

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓·········✓·····20✓·········✓·····35✓48✓56✓62✓74✓

i=2, a=0: satır i−1 kopyalanır, ayrıca dp[i−1][j−0] ✓ olan her j için ✓ → yeni toplam yok (0 eklemek hiçbir toplamı değiştirmez).

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓·········✓·····20✓·········✓·····35✓····✓····✓····✓48✓56✓62✓74✓

i=3, a=5: satır i−1 kopyalanır, ayrıca dp[i−1][j−5] ✓ olan her j için ✓ → yeni ulaşılan toplamlar: 5, 15.

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓·········✓·····20✓·········✓·····35✓····✓····✓····✓48✓····✓··✓·✓··✓·✓56✓62✓74✓

i=4, a=8: satır i−1 kopyalanır, ayrıca dp[i−1][j−8] ✓ olan her j için ✓ → yeni ulaşılan toplamlar: 8, 13.

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓·········✓·····20✓·········✓·····35✓····✓····✓····✓48✓····✓··✓·✓··✓·✓56✓····✓✓·✓·✓✓·✓✓✓62✓74✓

i=5, a=6: satır i−1 kopyalanır, ayrıca dp[i−1][j−6] ✓ olan her j için ✓ → yeni ulaşılan toplamlar: 6, 11, 14.

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓·········✓·····20✓·········✓·····35✓····✓····✓····✓48✓····✓··✓·✓··✓·✓56✓····✓✓·✓·✓✓·✓✓✓62✓·✓··✓✓✓✓·✓✓✓✓✓✓74✓

i=6, a=2: satır i−1 kopyalanır, ayrıca dp[i−1][j−2] ✓ olan her j için ✓ → yeni ulaşılan toplamlar: 2, 7, 12.

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓·········✓·····20✓·········✓·····35✓····✓····✓····✓48✓····✓··✓·✓··✓·✓56✓····✓✓·✓·✓✓·✓✓✓62✓·✓··✓✓✓✓·✓✓✓✓✓✓74✓·✓·✓✓✓✓✓✓✓✓✓✓✓✓

i=7, a=4: satır i−1 kopyalanır, ayrıca dp[i−1][j−4] ✓ olan her j için ✓ → yeni ulaşılan toplamlar: 4, 9.

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓·········✓·····20✓·········✓·····35✓····✓····✓····✓48✓····✓··✓·✓··✓·✓56✓····✓✓·✓·✓✓·✓✓✓62✓·✓··✓✓✓✓·✓✓✓✓✓✓74✓·✓·✓✓✓✓✓✓✓✓✓✓✓✓

dp[7][15] = dp[6][15] = ✓ → 4 olmadan da toplam 15 mümkün; seçilmez, yukarı çık.

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓·········✓·····20✓·········✓·····35✓····✓····✓····✓48✓····✓··✓·✓··✓·✓56✓····✓✓·✓·✓✓·✓✓✓62✓·✓··✓✓✓✓·✓✓✓✓✓✓74✓·✓·✓✓✓✓✓✓✓✓✓✓✓✓

dp[6][15] = dp[5][15] = ✓ → 2 olmadan da toplam 15 mümkün; seçilmez, yukarı çık.

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓·········✓·····20✓·········✓·····35✓····✓····✓····✓48✓····✓··✓·✓··✓·✓56✓····✓✓·✓·✓✓·✓✓✓62✓·✓··✓✓✓✓·✓✓✓✓✓✓74✓·✓·✓✓✓✓✓✓✓✓✓✓✓✓

dp[5][15] = dp[4][15] = ✓ → 6 olmadan da toplam 15 mümkün; seçilmez, yukarı çık.

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓·········✓·····20✓·········✓·····35✓····✓····✓····✓48✓····✓··✓·✓··✓·✓56✓····✓✓·✓·✓✓·✓✓✓62✓·✓··✓✓✓✓·✓✓✓✓✓✓74✓·✓·✓✓✓✓✓✓✓✓✓✓✓✓

dp[4][15] = dp[3][15] = ✓ → 8 olmadan da toplam 15 mümkün; seçilmez, yukarı çık.

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓·········✓·····20✓·········✓·····35✓····✓····✓····✓48✓····✓··✓·✓··✓·✓56✓····✓✓·✓·✓✓·✓✓✓62✓·✓··✓✓✓✓·✓✓✓✓✓✓74✓·✓·✓✓✓✓✓✓✓✓✓✓✓✓

dp[3][15] ✓ ama dp[2][15] · → 5 seçildi; j = 15 − 5 = 10

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓·········✓·····20✓·········✓·····35✓····✓····✓····✓48✓····✓··✓·✓··✓·✓56✓····✓✓·✓·✓✓·✓✓✓62✓·✓··✓✓✓✓·✓✓✓✓✓✓74✓·✓·✓✓✓✓✓✓✓✓✓✓✓✓

dp[2][10] = dp[1][10] = ✓ → 0 olmadan da toplam 10 mümkün; seçilmez, yukarı çık.

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓·········✓·····20✓·········✓·····35✓····✓····✓····✓48✓····✓··✓·✓··✓·✓56✓····✓✓·✓·✓✓·✓✓✓62✓·✓··✓✓✓✓·✓✓✓✓✓✓74✓·✓·✓✓✓✓✓✓✓✓✓✓✓✓

dp[1][10] ✓ ama dp[0][10] · → 10 seçildi; j = 10 − 10 = 0

j (toplam) →iaᵢ01234567891011121314150—✓···············110✓·········✓·····20✓·········✓·····35✓····✓····✓····✓48✓····✓··✓·✓··✓·✓56✓····✓✓·✓·✓✓·✓✓✓62✓·✓··✓✓✓✓·✓✓✓✓✓✓74✓·✓·✓✓✓✓✓✓✓✓✓✓✓✓

Geri izleme {5, 10} alt kümesini bulur: 10 + 5 = 15. {5, 8, 2} ve {5, 6, 4} de geçerlidir; tablo yalnızca birini verir.

Alt Küme Toplamı

Alt Küme Toplamı: Kod ve Analiz

boolean altKume(int[] a, int hedef) {
  int n = a.length;
  boolean[][] dp = new boolean[n + 1][hedef + 1];
  for (int i = 0; i <= n; i++)
    dp[i][0] = true;           // boş küme
  for (int i = 1; i <= n; i++) {
    int x = a[i - 1];
    for (int j = 1; j <= hedef; j++)
      dp[i][j] = dp[i - 1][j]  // x alınmaz
        || (x <= j && dp[i - 1][j - x]);
  }
  return dp[n][hedef];
}
  • Zaman ve bellek: O(n · hedef) — yine sözde-polinom.
  • Problem NP-tamdır; hedef büyükse tablo kurulamaz.
  • Bu örnekte 6 farklı çözüm var: {10, 5}, {5, 8, 2}, {5, 6, 4} ve bunların 0 eklenmiş hâlleri.
  • Tek boyutlu sürüm: j büyükten küçüğe gezilir (0/1 gibi).
  • Uygulama: eşit bölme problemi — toplamı S olan diziyi iki eşit parçaya bölmek = hedef S/2 ile alt küme toplamı.

En Uzun Artan Alt Dizi

En Uzun Artan Alt Dizi (Longest Increasing Subsequence)

  • Alt dizi (subsequence): elemanlar sırası korunarak, atlanarak seçilebilir. Alt dizgi / alt dizi parçası (substring, subarray): elemanlar bitişik olmalıdır.
  • LIS: elemanları kesin artan en uzun alt dizi.
0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 → LIS = 0, 2, 6, 9, 11, 15 (uzunluk 6)
O(n²) DPdp[i] = a[i]'de biten en uzun artan alt dizinin uzunluğu. dp[i] = 1 + max{ dp[j] : j < i, a[j] < a[i] }.
O(n log n): aktif listelerHer uzunluk için en küçük sonlu listeyi tut; yeni eleman ikili aramayla yerine konur.

Bu dizinin birden çok LIS'i vardır (ör. 0, 4, 6, 9, 13, 15); uzunluk her zaman tektir.

En Uzun Artan Alt Dizi

LIS: O(n²) Dinamik Programlama

0123456789101112131415a0841221061419513311715dpönceki

Başlangıç: her eleman tek başına uzunluk 1'lik artan alt dizidir.

0123456789101112131415a0841221061419513311715dp1önceki—

i=0: a[0]=0, öncesinde eleman yok → dp[0] = 1.

0123456789101112131415a0841221061419513311715dp12önceki—0

i=1: a[1]=8; küçük öncekiler (mavi) arasında en büyük dp, j=0 (dp=1) → dp[1] = 2

0123456789101112131415a0841221061419513311715dp122önceki—00

i=2: a[2]=4; küçük öncekiler (mavi) arasında en büyük dp, j=0 (dp=1) → dp[2] = 2

0123456789101112131415a0841221061419513311715dp1223önceki—001

i=3: a[3]=12; küçük öncekiler (mavi) arasında en büyük dp, j=1 (dp=2) → dp[3] = 3

0123456789101112131415a0841221061419513311715dp12232önceki—0010

i=4: a[4]=2; küçük öncekiler (mavi) arasında en büyük dp, j=0 (dp=1) → dp[4] = 2

0123456789101112131415a0841221061419513311715dp122323önceki—00101

i=5: a[5]=10; küçük öncekiler (mavi) arasında en büyük dp, j=1 (dp=2) → dp[5] = 3

0123456789101112131415a0841221061419513311715dp1223233önceki—001012

i=6: a[6]=6; küçük öncekiler (mavi) arasında en büyük dp, j=2 (dp=2) → dp[6] = 3

0123456789101112131415a0841221061419513311715dp12232334önceki—0010123

i=7: a[7]=14; küçük öncekiler (mavi) arasında en büyük dp, j=3 (dp=3) → dp[7] = 4

0123456789101112131415a0841221061419513311715dp122323342önceki—00101230

i=8: a[8]=1; küçük öncekiler (mavi) arasında en büyük dp, j=0 (dp=1) → dp[8] = 2

0123456789101112131415a0841221061419513311715dp1223233424önceki—001012306

i=9: a[9]=9; küçük öncekiler (mavi) arasında en büyük dp, j=6 (dp=3) → dp[9] = 4

0123456789101112131415a0841221061419513311715dp12232334243önceki—0010123062

i=10: a[10]=5; küçük öncekiler (mavi) arasında en büyük dp, j=2 (dp=2) → dp[10] = 3

0123456789101112131415a0841221061419513311715dp122323342435önceki—00101230629

i=11: a[11]=13; küçük öncekiler (mavi) arasında en büyük dp, j=9 (dp=4) → dp[11] = 5

0123456789101112131415a0841221061419513311715dp1223233424353önceki—001012306294

i=12: a[12]=3; küçük öncekiler (mavi) arasında en büyük dp, j=4 (dp=2) → dp[12] = 3

0123456789101112131415a0841221061419513311715dp12232334243535önceki—0010123062949

i=13: a[13]=11; küçük öncekiler (mavi) arasında en büyük dp, j=9 (dp=4) → dp[13] = 5

0123456789101112131415a0841221061419513311715dp122323342435354önceki—00101230629496

i=14: a[14]=7; küçük öncekiler (mavi) arasında en büyük dp, j=6 (dp=3) → dp[14] = 4

0123456789101112131415a0841221061419513311715dp1223233424353546önceki—0010123062949611

i=15: a[15]=15; küçük öncekiler (mavi) arasında en büyük dp, j=11 (dp=5) → dp[15] = 6

0123456789101112131415a0841221061419513311715dp1223233424353546önceki—0010123062949611

En büyük dp = 6 (i=15). önceki indisleri izlenir: 15 → 11 → 9 → 6 → 2 → 0 ⇒ 0, 4, 6, 9, 13, 15 (eşitlikte ilk j seçilir; 0, 2, 6, 9, 11, 15 de 6 uzunlukta bir LIS'tir).

Turuncu: işlenen eleman · mavi: a[j] < a[i] olan adaylar · yeşil: en iyi önceki eleman. Toplam O(n²) karşılaştırma.

En Uzun Artan Alt Dizi

LIS: Aktif Listeler Yöntemi

Her uzunluk için sonu en küçük olan artan liste tutulur; küçük son, gelecekte uzamaya daha açıktır.

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,150

Liste yok: [0] ile ilk aktif liste başlatılır.

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 8

8 tüm sonlardan büyük: en uzun liste kopyalanıp 8 eklenir (uzunluk 2).

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 40, 8pasif (atıldı)

4, sonu 0 olan listeyi uzatır → [0, 4]; aynı uzunluktaki [0, 8] artık gereksiz (sonu daha büyük).

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 40, 4, 12

12 tüm sonlardan büyük: en uzun liste kopyalanıp 12 eklenir (uzunluk 3).

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 20, 4pasif (atıldı)0, 4, 12

2, sonu 0 olan listeyi uzatır → [0, 2]; aynı uzunluktaki [0, 4] artık gereksiz (sonu daha büyük).

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 20, 2, 100, 4, 12pasif (atıldı)

10, sonu 2 olan listeyi uzatır → [0, 2, 10]; aynı uzunluktaki [0, 4, 12] artık gereksiz (sonu daha büyük).

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 20, 2, 60, 2, 10pasif (atıldı)

6, sonu 2 olan listeyi uzatır → [0, 2, 6]; aynı uzunluktaki [0, 2, 10] artık gereksiz (sonu daha büyük).

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 20, 2, 60, 2, 6, 14

14 tüm sonlardan büyük: en uzun liste kopyalanıp 14 eklenir (uzunluk 4).

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 10, 2pasif (atıldı)0, 2, 60, 2, 6, 14

1, sonu 0 olan listeyi uzatır → [0, 1]; aynı uzunluktaki [0, 2] artık gereksiz (sonu daha büyük).

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 10, 2, 60, 2, 6, 90, 2, 6, 14pasif (atıldı)

9, sonu 6 olan listeyi uzatır → [0, 2, 6, 9]; aynı uzunluktaki [0, 2, 6, 14] artık gereksiz (sonu daha büyük).

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 10, 1, 50, 2, 6pasif (atıldı)0, 2, 6, 9

5, sonu 1 olan listeyi uzatır → [0, 1, 5]; aynı uzunluktaki [0, 2, 6] artık gereksiz (sonu daha büyük).

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 10, 1, 50, 2, 6, 90, 2, 6, 9, 13

13 tüm sonlardan büyük: en uzun liste kopyalanıp 13 eklenir (uzunluk 5).

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 10, 1, 30, 1, 5pasif (atıldı)0, 2, 6, 90, 2, 6, 9, 13

3, sonu 1 olan listeyi uzatır → [0, 1, 3]; aynı uzunluktaki [0, 1, 5] artık gereksiz (sonu daha büyük).

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 10, 1, 30, 2, 6, 90, 2, 6, 9, 110, 2, 6, 9, 13pasif (atıldı)

11, sonu 9 olan listeyi uzatır → [0, 2, 6, 9, 11]; aynı uzunluktaki [0, 2, 6, 9, 13] artık gereksiz (sonu daha büyük).

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 10, 1, 30, 1, 3, 70, 2, 6, 9pasif (atıldı)0, 2, 6, 9, 11

7, sonu 3 olan listeyi uzatır → [0, 1, 3, 7]; aynı uzunluktaki [0, 2, 6, 9] artık gereksiz (sonu daha büyük).

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 10, 1, 30, 1, 3, 70, 2, 6, 9, 110, 2, 6, 9, 11, 15

15 tüm sonlardan büyük: en uzun liste kopyalanıp 15 eklenir (uzunluk 6).

0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,1500, 10, 1, 30, 1, 3, 70, 2, 6, 9, 110, 2, 6, 9, 11, 15← en uzun artan alt dizi

Dizi bitti: en uzun aktif liste 0, 2, 6, 9, 11, 15 → LIS uzunluğu 6.

En Uzun Artan Alt Dizi

LIS: O(n²) Kod

static int lis(int[] a) {
  int n = a.length, enUzun = 0;
  int[] dp = new int[n];
  for (int i = 0; i < n; i++) {
    dp[i] = 1;               // tek başına
    for (int j = 0; j < i; j++)
      if (a[j] < a[i] && dp[j] + 1 > dp[i])
        dp[i] = dp[j] + 1;   // uzat
    enUzun = Math.max(enUzun, dp[i]);
  }
  return enUzun;
}
  • İç döngü, a[i]'den küçük her önceki elemanın dizisini uzatmayı dener.
  • Zaman O(n²), bellek O(n).
  • Diziyi kurmak için onceki[i] = j saklanır ya da sondan başa dp değerleri azalarak taranır.
  • Cevap dp[n−1] değil, max dp[i]'dir: LIS herhangi bir yerde bitebilir.

En Uzun Artan Alt Dizi

LIS: O(n log n) — Sonlar Dizisi ve İkili Arama

static int lisHizli(int[] a) {
  int[] son = new int[a.length];
  int uz = 0;                // aktif liste sayısı
  for (int x : a) {
    int alt = 0, ust = uz;   // son[alt] >= x ilk yer
    while (alt < ust) {
      int orta = (alt + ust) / 2;
      if (son[orta] < x) alt = orta + 1;
      else ust = orta;
    }
    son[alt] = x;            // yerine koy / uzat
    if (alt == uz) uz++;
  }
  return uz;
}
  • Aktif listelerin yalnızca sonları saklanır; son[] her zaman artan sıradadır, yer ikili aramayla bulunur → O(n log n).
  • Örnek dizide son = [0, 1, 3, 7, 11, 15]: uzunluk doğru (6) ama bu dizi bir LIS değildir (7, 11'den sonra gelir).
  • Diziyi kurmak için her elemanın önceki indisi ayrıca tutulur.

En Uzun Ortak Alt Dizgi

En Uzun Ortak Alt Dizgi (Longest Common Substring)

  • İki dizginin bitişik karakterlerden oluşan en uzun ortak parçası.
  • Örnek: "abdca" ve "cbda" → ortak alt dizgiler "a", "b", "d", "c", "bd" → cevap "bd", uzunluk 2.
  • Durum: L[i][j] = Y'nin ilk i ve X'in ilk j karakterinin tam sonunda biten en uzun ortak parçanın uzunluğu.
Y[i−1] = X[j−1] ise L[i][j] = L[i−1][j−1] + 1
aksi hâlde L[i][j] = 0 (bitişiklik bozulur, sayaç sıfırlanır)
Cevap = tablodaki en büyük değer (son hücre değil!)

Kaba kuvvet: X'in O(m²) alt dizgisini Y içinde aramak → O(m² · n). DP: O(m · n) zaman.

En Uzun Ortak Alt Dizgi

Ortak Alt Dizgi: Tabloyu Adım Adım Doldur

Sütunlar X = "abdca", satırlar Y = "cbda". Turuncu: hesaplanan hücre · mavi: kullanılan çapraz hücre.

abdca0000000c10b20d30a40012345

Satır 0 ve sütun 0 sıfırdır: boş dizgiyle ortak parça yoktur.

abdca0000000c100b20d30a40012345

'c' ≠ 'a' → L[1][1] = 0 · en iyi: 0

abdca0000000c1000b20d30a40012345

'c' ≠ 'b' → L[1][2] = 0 · en iyi: 0

abdca0000000c10000b20d30a40012345

'c' ≠ 'd' → L[1][3] = 0 · en iyi: 0

abdca0000000c100001b20d30a40012345

Y[0]='c' = X[3]='c' → L[1][4] = L[0][3] + 1 = 1 · en iyi: 1

abdca0000000c1000010b20d30a40012345

'c' ≠ 'a' → L[1][5] = 0 · en iyi: 1

abdca0000000c1000010b200d30a40012345

'b' ≠ 'a' → L[2][1] = 0 · en iyi: 1

abdca0000000c1000010b2001d30a40012345

Y[1]='b' = X[1]='b' → L[2][2] = L[1][1] + 1 = 1 · en iyi: 1

abdca0000000c1000010b20010d30a40012345

'b' ≠ 'd' → L[2][3] = 0 · en iyi: 1

abdca0000000c1000010b200100d30a40012345

'b' ≠ 'c' → L[2][4] = 0 · en iyi: 1

abdca0000000c1000010b2001000d30a40012345

'b' ≠ 'a' → L[2][5] = 0 · en iyi: 1

abdca0000000c1000010b2001000d300a40012345

'd' ≠ 'a' → L[3][1] = 0 · en iyi: 1

abdca0000000c1000010b2001000d3000a40012345

'd' ≠ 'b' → L[3][2] = 0 · en iyi: 1

abdca0000000c1000010b2001000d30002a40012345

Y[2]='d' = X[2]='d' → L[3][3] = L[2][2] + 1 = 2 · en iyi: 2

abdca0000000c1000010b2001000d300020a40012345

'd' ≠ 'c' → L[3][4] = 0 · en iyi: 2

abdca0000000c1000010b2001000d3000200a40012345

'd' ≠ 'a' → L[3][5] = 0 · en iyi: 2

abdca0000000c1000010b2001000d3000200a401012345

Y[3]='a' = X[0]='a' → L[4][1] = L[3][0] + 1 = 1 · en iyi: 2

abdca0000000c1000010b2001000d3000200a4010012345

'a' ≠ 'b' → L[4][2] = 0 · en iyi: 2

abdca0000000c1000010b2001000d3000200a40100012345

'a' ≠ 'd' → L[4][3] = 0 · en iyi: 2

abdca0000000c1000010b2001000d3000200a401000012345

'a' ≠ 'c' → L[4][4] = 0 · en iyi: 2

abdca0000000c1000010b2001000d3000200a4010001012345

Y[3]='a' = X[4]='a' → L[4][5] = L[3][4] + 1 = 1 · en iyi: 2

abdca0000000c1000010b2001000d3000200a4010001012345

En büyük değer L[3][3] = 2; çapraz geriye 2 adım: X[1..2] = "bd". Son hücre L[4][5] = 1 cevap değildir.

En Uzun Ortak Alt Dizi

En Uzun Ortak Alt Dizi (Longest Common Subsequence)

  • İki dizinin, sırası korunarak (atlamalı) seçilebilen en uzun ortak alt dizisi.
  • Örnek: "abdca" ve "cbda" → "bda", uzunluk 3 (ortak alt dizgi yalnızca 2 idi).
  • Kullanım: diff / git karşılaştırması, DNA dizilerinin benzerliği, intihal tespiti.
Y[i−1] = X[j−1] ise L[i][j] = L[i−1][j−1] + 1 (ortak karakteri al)
aksi hâlde L[i][j] = max( L[i−1][j] , L[i][j−1] ) (birini atla)
Cevap = L[n][m] (son hücre)
EşleşmeEşleşmemeCevap
Ortak alt dizgiçapraz + 10tablonun maksimumu
Ortak alt dizi (LCS)çapraz + 1max(üst, sol)son hücre

En Uzun Ortak Alt Dizi

LCS: Kodu Adım Adım İzle

abdca0000000c10b20d30a40012345

L dizisi (n+1) × (m+1) = 5 × 6; Java'da başlangıçta tüm hücreler 0.

abdca0000000c100b20d30a40012345

'c' ≠ 'a' → max(üst 0, sol 0) = 0

abdca0000000c1000b20d30a40012345

'c' ≠ 'b' → max(üst 0, sol 0) = 0

abdca0000000c10000b20d30a40012345

'c' ≠ 'd' → max(üst 0, sol 0) = 0

abdca0000000c100001b20d30a40012345

'c' = 'c' → çapraz + 1: L[1][4] = 0 + 1 = 1

abdca0000000c1000011b20d30a40012345

'c' ≠ 'a' → max(üst 0, sol 1) = 1

abdca0000000c1000011b200d30a40012345

'b' ≠ 'a' → max(üst 0, sol 0) = 0

abdca0000000c1000011b2001d30a40012345

'b' = 'b' → çapraz + 1: L[2][2] = 0 + 1 = 1

abdca0000000c1000011b20011d30a40012345

'b' ≠ 'd' → max(üst 0, sol 1) = 1

abdca0000000c1000011b200111d30a40012345

'b' ≠ 'c' → max(üst 1, sol 1) = 1

abdca0000000c1000011b2001111d30a40012345

'b' ≠ 'a' → max(üst 1, sol 1) = 1

abdca0000000c1000011b2001111d300a40012345

'd' ≠ 'a' → max(üst 0, sol 0) = 0

abdca0000000c1000011b2001111d3001a40012345

'd' ≠ 'b' → max(üst 1, sol 0) = 1

abdca0000000c1000011b2001111d30012a40012345

'd' = 'd' → çapraz + 1: L[3][3] = 1 + 1 = 2

abdca0000000c1000011b2001111d300122a40012345

'd' ≠ 'c' → max(üst 1, sol 2) = 2

abdca0000000c1000011b2001111d3001222a40012345

'd' ≠ 'a' → max(üst 1, sol 2) = 2

abdca0000000c1000011b2001111d3001222a401012345

'a' = 'a' → çapraz + 1: L[4][1] = 0 + 1 = 1

abdca0000000c1000011b2001111d3001222a4011012345

'a' ≠ 'b' → max(üst 1, sol 1) = 1

abdca0000000c1000011b2001111d3001222a40112012345

'a' ≠ 'd' → max(üst 2, sol 1) = 2

abdca0000000c1000011b2001111d3001222a401122012345

'a' ≠ 'c' → max(üst 2, sol 2) = 2

abdca0000000c1000011b2001111d3001222a4011223012345

'a' = 'a' → çapraz + 1: L[4][5] = 2 + 1 = 3

abdca0000000c1000011b2001111d3001222a4011223012345

Tablo doldu: L[4][5] = 3. 20 hücre, her biri O(1) → O(m · n).

static int lcs(String x, String y) {
  int m = x.length(), n = y.length();
  int[][] L = new int[n + 1][m + 1];
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++)
      if (y.charAt(i - 1) == x.charAt(j - 1))
        L[i][j] = L[i - 1][j - 1] + 1;
      else
        L[i][j] = Math.max(L[i - 1][j],
                           L[i][j - 1]);
  return L[n][m];
}

En Uzun Ortak Alt Dizi

LCS: Diziyi Geri İzleyerek Bulma

abdca0000000c1000011b2001111d3001222a4011223012345

Geri izleme son hücreden, L[4][5] = 3'ten başlar; bulunan karakterler sondan başa toplanır.

abdca0000000c1000011b2001111d3001222a4011223012345

'a' = 'a' → karakter LCS'ye eklenir ("a"), çapraz geçilir.

abdca0000000c1000011b2001111d3001222a4011223012345

'd' ≠ 'c'; sol (2) > üst (1) → sola gidilir.

abdca0000000c1000011b2001111d3001222a4011223012345

'd' = 'd' → karakter LCS'ye eklenir ("da"), çapraz geçilir.

abdca0000000c1000011b2001111d3001222a4011223012345

'b' = 'b' → karakter LCS'ye eklenir ("bda"), çapraz geçilir.

abdca0000000c1000011b2001111d3001222a4011223012345

'c' ≠ 'a'; üst (0) ≥ sol (0) → yukarı çıkılır.

abdca0000000c1000011b2001111d3001222a4011223012345

Sınıra ulaşıldı. Yeşil hücreler eşleşmeler: LCS = "bda", uzunluk 3. Geri izleme O(m + n).

En Uzun Ortak Alt Dizi

Deneyin: LCS ve Ortak Alt Dizgi Tablosu

X en fazla 9, Y en fazla 7 karakter. Yeşil hücreler geri izleme yolunu gösterir. Düzenleme uzaklığı: ekleme, silme ve değiştirme 1 birim.

Diğer Problemler

Diğer Klasik DP Problemleri

ProblemDurum ve bağıntıSüre
Düzenleme uzaklığı (edit distance)D[i][j] = min(D[i−1][j] + 1, D[i][j−1] + 1, D[i−1][j−1] + [aᵢ ≠ bⱼ]); D[i][0] = i, D[0][j] = jO(m · n)
Para üstü (coin change)en az para: P[x] = 1 + min P[x − c] (c ≤ x); P[0] = 0O(k · X)
En büyük toplamlı alt diziV(i) = max(a[i], V(i−1) + a[i]) — Kadane; cevap max V(i)O(n)
Matris zinciri çarpımıM[i][j] = min { M[i][k] + M[k+1][j] + pᵢ₋₁ · pₖ · pⱼ : i ≤ k < j }O(n³)
Gezgin satıcı (Held-Karp)dp[S][i] = S kümesini gezip i'de biten en kısa yol; dp[S][i] = min dp[S − i][j] + d(j, i)O(n² · 2ⁿ)
Bellman-Forddₖ(v) = en fazla k kenarlı en kısa yol; dₖ(v) = min(dₖ₋₁(v), dₖ₋₁(u) + w(u, v))O(V · E)
Optimal ikili arama ağacıe[i][j] = min { e[i][r−1] + e[r+1][j] : i ≤ r ≤ j } + w(i, j)O(n³)

Özet

Özet

ProblemDurum / bağıntıZaman · bellek
FibonacciF(n) = F(n−1) + F(n−2); memoization ya da tabloO(n) · O(1)
0/1 sırt çantasıK[i][c] = max(K[i−1][c], vᵢ + K[i−1][c−wᵢ])O(nW) · O(W)
Sınırsız sırt çantasıdp[i][c] = max(dp[i−1][c], pᵢ + dp[i][c−wᵢ])O(nW) · O(W)
Alt küme toplamıdp[i][j] = dp[i−1][j] ∨ dp[i−1][j−aᵢ]O(n · hedef)
LISdp[i] = 1 + max dp[j] (j < i, a[j] < a[i]); ya da aktif listelerO(n²) / O(n log n)
Ortak alt dizgieşitse çapraz + 1, değilse 0; cevap tablonun maksimumuO(mn)
LCSeşitse çapraz + 1, değilse max(üst, sol); cevap son hücreO(mn)
DP = örtüşen alt problemler + optimal alt yapı. Süre ≈ durum sayısı × durum başına iş. Çözümün kendisi tablodan geri izlenerek bulunur.

Özet

Sık Yapılan Hatalar

Taban durumlarını unutmakK[0][c], dp[i][0] ya da fib(0)/fib(1) yanlış başlatılırsa bütün tablo kayar.
İndis kaymasıTablo (n+1) boyutlu ama dizi n: i. satır v[i − 1]'i kullanır. v[i] yazmak son eşyada taşma verir.
Yanlış döngü yönüTek boyutlu 0/1 sırt çantasında kapasiteyi küçükten büyüğe gezmek eşyayı birden çok kez alır (sınırsız olur).
Alt dizgi ≠ alt diziOrtak alt dizgide eşleşmeme 0 yazar ve cevap tablonun maksimumudur; LCS'de max(üst, sol) yazılır ve cevap son hücredir.
Açgözlü sanmak0/1 sırt çantasında ve para üstünde ({1, 3, 4} ile 6) açgözlü seçim optimal değildir.
O(nW)'yi polinom sanmakW sayısal değerdir; girdi boyutu log W bittir. Büyük W'de tablo kurulamaz.

Özet

Örnek Problemler

  1. 0/1 sırt çantası: W = 5; eşyalar (v, w) = (3, 2), (4, 3), (5, 4), (6, 5). En büyük değer?
  2. LCS("ABCBDAB", "BDCABA") uzunluğu kaçtır? Bir LCS yazın.
  3. Paralar {1, 3, 4}, tutar 6: en az kaç para? Açgözlü kaç para kullanır?
  4. [10, 22, 9, 33, 21, 50, 41, 60] dizisinin LIS uzunluğu ve bir LIS?
  5. "kitten" → "sitting" düzenleme uzaklığı?
  6. Düz özyinelemeli fib(6) kaç çağrı yapar? Memoization ile kaç?
Cevapları göster
  1. 7: eşya 1 + eşya 2 (w = 2 + 3 = 5, v = 3 + 4).
  2. 4; ör. "BCBA" (ya da "BDAB", "BCAB").
  3. DP: 2 para (3 + 3). Açgözlü: 4 + 1 + 1 = 3 para.
  4. 5: 10, 22, 33, 50, 60 (ya da 10, 22, 33, 41, 60).
  5. 3: k→s değiştir, e→i değiştir, sona g ekle.
  6. 2 · F(7) − 1 = 2 · 13 − 1 = 25 çağrı; memoization ile 2 · 6 − 1 = 11.

Özet

Alıştırmalar

  1. 0/1 sırt çantasını memoization (yukarıdan aşağı) ile yazın.
  2. Paralar {1, 2, 5} ile 5 tutarı kaç farklı şekilde ödenebilir?
  3. "ababc" ve "babca" için en uzun ortak alt dizgiyi bulun.
  4. {3, 34, 4, 12, 5, 2} kümesinde toplamı 9 ve 30 olan alt küme var mı?
  5. [−2, 1, −3, 4, −1, 2, 1, −5, 4] için en büyük toplamlı alt dizi (Kadane)?
  6. Ağırlık {2, 3, …, 8}, değer {3, 5, 8, 6, 10, 11, 15}, W = 10: 0/1 ve sınırsız sonuçlar?
Cevaplar / ipuçları
  1. Integer[][] bellek; f(i, c): i = n ise 0; aksi hâlde max(f(i+1, c), v[i] + f(i+1, c − w[i])), sonucu kaydet.
  2. 4: 5, 2+2+1, 2+1+1+1, 1·5 (dış döngü paralar üzerinde: d[x] += d[x − c]).
  3. "babc", uzunluk 4.
  4. 9: evet (4 + 5). 30: hayır (34 hariç toplam 26 < 30).
  5. 6: [4, −1, 2, 1].
  6. 0/1: 18 (ör. w 4 + 6 → 8 + 10). Sınırsız: 19 (ör. w 4 + 4 + 2 → 8 + 8 + 3).

Özet

Kaynaklar

  • Cormen, Leiserson, Rivest ve Stein, Introduction to Algorithms, 4. baskı, MIT Press, 2022 (Bölüm 14: Dynamic Programming).
  • Kleinberg ve Tardos, Algorithm Design, Pearson, 2005 (Bölüm 6).
  • Dasgupta, Papadimitriou ve Vazirani, Algorithms, McGraw-Hill, 2006 (Bölüm 6).
  • R. Bellman, Dynamic Programming, Princeton University Press, 1957.

Son

Bölüm 6: Dinamik Programlama
Sonraki bölüm: Bölüm 7: Yakınsama Algoritmaları
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.