0/1ナップサック問題

🔥 Vibe プロンプト

「DPで0/1ナップサックを解決:N=5、W=10、アイテム=[(2,6),(5,9),(4,5),(3,7),(6,8)]。DPテーブルと選択アイテムを出力。」

0/1ナップサックとは?

0/1ナップサックは、重さと価値を持つN個のアイテムから、容量Wに収まるように選択して総価値を最大化するDPの古典的問題です。各アイテムは0回か1回だけ選べます。

| 性質 | 意味 | |------|------| | 状態 | dp[i][w] = 最初のi個のアイテム、重さ≤wでの最大価値 | | 漸化式 | dp[i][w] = max(スキップ, 採用) = max(dp[i-1][w], dp[i-1][w-wi] + vi) | | 初期 | dp[0][w] = 0, dp[i][0] = 0 | | 時間 | O(N × W) — 疑似多項式時間 | | 空間 | O(N × W) → 最適化後 O(W) |

実装

def knapsack_01(weights, values, capacity):
    """
    weights: 各アイテムの重さ
    values: 各アイテムの価値
    capacity: 最大重量
    戻り値: (最大価値, 選択されたインデックスリスト)
    """
    n = len(weights)
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    
    # DPテーブルを埋める
    for i in range(1, n + 1):
        wi, vi = weights[i-1], values[i-1]
        for w in range(1, capacity + 1):
            if wi <= w:
                # 選択肢1: スキップ | 選択肢2: 採用
                dp[i][w] = max(dp[i-1][w], dp[i-1][w-wi] + vi)
            else:
                dp[i][w] = dp[i-1][w]  # 入らない
    
    # バックトラックで選択アイテムを特定
    selected = []
    w = capacity
    for i in range(n, 0, -1):
        if dp[i][w] != dp[i-1][w]:  # アイテムi-1が採用された
            selected.append(i-1)
            w -= weights[i-1]
    
    return dp[n][capacity], selected

# 例: 5アイテム、容量10
weights = [2, 5, 4, 3, 6]
values = [6, 9, 5, 7, 8]
capacity = 10

max_val, items = knapsack_01(weights, values, capacity)

print(f"\n{'='*45}")
print(f"0/1ナップサック結果")
print(f"{'='*45}")
print(f"最大価値: {max_val}")
print(f"選択アイテム: {items}")
total_w = sum(weights[i] for i in items)
print(f"総重量: {total_w}/{capacity}")

# DPテーブル出力
print(f"\nDPテーブル:")
for i in range(n + 1):
    row = " ".join(f"{dp[i][w]:2}" for w in range(capacity + 1))
    print(f"i={i}: [{row}]")

空間最適化版

def knapsack_opt(weights, values, capacity):
    """O(W)空間、O(N×W)時間"""
    dp = [0] * (capacity + 1)
    for i in range(len(weights)):
        wi, vi = weights[i], values[i]
        for w in range(capacity, wi - 1, -1):  # 逆順が重要!
            dp[w] = max(dp[w], dp[w-wi] + vi)
    return dp[capacity]

print(f"ナップサック(O(W)空間): {knapsack_opt(weights, values, capacity)}")

まとめ

| 項目 | 詳細 | |------|------| | 状態定義 | dp[i][w] = i個のアイテム、重さ≤wでの最大価値 | | 漸化式 | dp[i][w] = max(dp[i-1][w], dp[i-1][w-wi] + vi) | | 初期条件 | dp[0][] = 0, dp[][0] = 0 | | 時間計算量 | O(N×W) | | 空間計算量 | O(N×W) → 最適化後 O(W) | | バックトラック | dp[n][W]から逆順に選択アイテムを特定 |

会員限定無料チュートリアル

このチャプターは登録会員限定の無料コンテンツです!ログインまたは登録してすぐにロックを解除してください。

今すぐログイン / 登録