0/1 Sırt Çantası (Knapsack)
Sınırlı kapasiteli bir çantaya en yüksek toplam değeri sığdırmak için her eşyayı almak ya da almamak arasında karar veren DP tablosu.
Önce bunlara göz at
#Nasıl çalışır?
0/1 sırt çantası (knapsack) probleminde her eşyanın bir ağırlığı ve değeri vardır; çantanın taşıyabileceği ağırlık sınırlıdır. Her eşya ya alınır ya alınmaz (0/1) ve toplam değer en yükseğe çıkarılmak istenir.
Açgözlü yaklaşım (ör. değer/ağırlık oranı en yüksek olanı almak) bu problemde yanılabilir. Tüm alt kümeleri denemek ise 2ⁿ seçenek demektir.
DP tablosunda dp[i][c], ilk i eşyayı kullanarak c kapasiteyle elde edilebilecek en yüksek değerdir. Her hücre için iki seçenek vardır: i. eşyayı almamak (dp[i − 1][c]) ya da sığıyorsa almak (dp[i − 1][c − w] + v). Büyük olan yazılır.
Tablo dolduktan sonra sağ alttan geri yürünür: bir hücre üstündekinden farklıysa o eşya alınmıştır.
#Kendin dene
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|---|
| — | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1: w1 v1 | ||||||||
| 2: w3 v4 | ||||||||
| 3: w4 v5 | ||||||||
| 4: w5 v7 |
Satırlar eşyalar (w: ağırlık, v: değer), sütunlar çanta kapasitesi. Her hücre, o eşyalara kadar ve o kapasiteyle alınabilecek en yüksek değeri tutar.
#Adım adım
- 01dp[0][c] = 0: hiç eşya yokken değer 0.
- 02Her eşya i (ağırlık w, değer v) ve her kapasite c için: almamak dp[i − 1][c].
- 03w ≤ c ise almak dp[i − 1][c − w] + v; ikisinden büyüğünü dp[i][c]'ye yaz.
- 04dp[n][W] cevaptır; seçilen eşyaları bulmak için sağ alttan geri izle.
#Örnek kod
Aynı algoritma 8 dilde. Her program derlenip çalıştırılarak test edildi ve hepsi aynı çıktıyı veriyor.
# (weight, value) of each item.
ITEMS = [(1, 1), (3, 4), (4, 5), (5, 7)]
W = 7
n = len(ITEMS)
# dp[i][c] = best value using the first i items with capacity c.
dp = [[0] * (W + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
w, v = ITEMS[i - 1]
for c in range(W + 1):
dp[i][c] = dp[i - 1][c] # skip item i
if w <= c:
dp[i][c] = max(dp[i][c], dp[i - 1][c - w] + v) # or take it
# Walk back: a value that differs from the row above means item i was taken.
taken, c = [], W
for i in range(n, 0, -1):
if dp[i][c] != dp[i - 1][c]:
taken.append(i)
c -= ITEMS[i - 1][0]
print(dp[n][W])
print(" ".join(map(str, reversed(taken)))) # 1-based item numbers#Karmaşıklık
| İşlem | Süre |
|---|---|
| Zaman | O(n · W) |
| Bellek (tam tablo) | O(n · W) |
| Bellek (tek satır) | O(W) |
| Tüm alt kümeleri denemek | O(2ⁿ) |
n eşya sayısı, W kapasitedir. O(n · W) polinom gibi görünse de W'nin sayısal değerine bağlıdır (sözde-polinom): kapasite çok büyükse tablo da büyür. Tek satırlık bir diziyle, kapasiteyi büyükten küçüğe dolaşarak bellek O(W)'ye indirilebilir.
#Ne zaman kullanılır?
Bütçe ve kaynak dağıtımı, yatırım portföyü seçimi, kargo yükleme, sınav için konu seçimi gibi "sınırlı kapasiteyle en çok değeri seç" problemleri. Aynı tablo fikri alt küme toplamı (subset sum) ve bozuk para problemlerinde de kullanılır.
#Alıştırma
Soruları cevaplamak ve ilerlemeni kaydetmek için giriş yap.
Kolay
0/1 sırt çantası probleminde "0/1" ne anlama gelir?
- 1Her eşya ya tamamen alınır ya hiç alınmaz
- 2Değerler 0 ya da 1'dir
- 3Kapasite 0 ya da 1'dir
- 4Eşyalar bölünebilir
dp[i][c] neyi tutar?
- 1İlk i eşyayla c kapasitede elde edilebilecek en yüksek değeri
- 2i. eşyanın ağırlığını
- 3c ağırlığındaki eşya sayısını
- 4Kalan kapasiteyi
Bu DP çözümünün zaman karmaşıklığı nedir (n eşya, W kapasite)?
- 1O(n · W)
- 2O(2ⁿ)
- 3O(n log n)
- 4O(W²)
Orta
Kapasite 7; eşyalar (ağırlık, değer): (3, 4), (4, 5), (5, 7). En yüksek değer kaçtır?
- 19
- 27
- 312
- 411
Zor
Bellek O(W)'ye indirilirken tek satırlık dizide kapasite neden büyükten küçüğe dolaşılır?
- 1Aynı eşyanın aynı turda iki kez alınmasını önlemek için
- 2Daha hızlı olduğu için
- 3Negatif değerleri önlemek için
- 4Dizi sıralı kalsın diye
#Bundan sonra
Bu dersi bildiğini varsayan dersler: