プログラム 技術

ナップザック問題のC++実装手順

今回は趣向を変えてみてナップザック問題をC++と動的計画法で解いてみます
ナップザック問題については以下のサイトが参考になるかと思います

参考サイト
ナップサック問題を動的計画法で解く - 具体例で学ぶ数学
ナップサック問題を動的計画法で解く - 具体例で学ぶ数学

ナップサック問題の問題設定と、動的計画法(表を順々に埋めていく方法)による解き方を解説します。 ナップサック問

続きを読む

0-1ナップザック問題(アイテムを1つだけ選べる場合)

とりあえず先にコードを起こしてみます

// アイテムを表す構造体
struct Item {
    int value;  // アイテムの価値
    int weight; // アイテムの重さ
};

int Knapsack01(std::vector<Item>& items, int capacity)
{
    // アイテムの数
    int n = items.size();
    // 動的計画法で使用するテーブル
    std::vector<std::vector<int>> dp(n + 1, std::vector<int>(capacity + 1));

    // テーブルを埋めていく
    for (int i = 0; i <= n; i++) {
        for (int w = 0; w <= capacity; w++) {
            if (i == 0 || w == 0) {
                dp[i][w] = 0; // 初期条件
            } else if (items[i - 1].weight <= w) {
                // i番目のアイテムを選ぶ場合と選ばない場合で、価値が大きい方を選ぶ
                dp[i][w] = std::max(items[i - 1].value + dp[i - 1][w - items[i - 1].weight], dp[i - 1][w]);
            } else {
                // i番目のアイテムは選べないので、i-1番目までのアイテムで価値を最大化
                dp[i][w] = dp[i - 1][w];
            }
        }
    }

    // ナップザックの最大価値を返す
    return dp[n][capacity];
}

int main() {
    std::vector<Item> items = { {10, 60}, {20, 100}, {30, 120} }; // アイテムの重さと価値
    int capacity = 50; // ナップザックの容量

    int max_value = Knapsack01(items, capacity);

    std::cout << "得られる最大値(Knapsack01): " << max_value << std::endl;
}

dp[i][w]は「最初のi個のアイテムの中からいくつか選んで、その重さの合計がw以下になるようにするときの、価値の合計の最大値」を表しています。ループの中で、i番目のアイテムをナップザックに入れるかどうかを決めます。
dp[i][w] = dp[i - 1][w]では現在のアイテムを入れるとナップザックの容量を超えてしまうので、そのアイテムは入れません。
dp[i][w] = std::max(items[i - 1].value + dp[i - 1][w - items[i - 1].weight], dp[i - 1][w])では、そのアイテムを入れるか入れないか、価値の合計が大きくなる方を選びます。
最終的にdp[n][capacity]には、全てのアイテムからいくつか選んでナップザックの容量capacityを超えないようにしたときの、価値の合計の最大値を格納しています

無制限ナップサック問題(各アイテムを何度でも選ぶことができる場合)

続いて、同じナップザック問題ですが少し勝手がかわるものになります

int UnboundedKnapsack(std::vector<Item>& items, int capacity) {
    int n = items.size();
    // 動的計画法で使用するテーブル
    std::vector<int> dp(capacity + 1, 0);

    // テーブルを埋めていく
    for (int w = 0; w <= capacity; w++) {
        for (int i = 0; i < n; i++) {
            if (items[i].weight <= w) {
                // i番目のアイテムを選ぶ場合と選ばない場合で、価値が大きい方を選ぶ
                dp[w] = std::max(dp[w], items[i].value + dp[w - items[i].weight]);
            }
        }
    }

    // ナップザックの最大価値を返す
    return dp[capacity];
}

int main() {
    std::vector<Item> items = { {2, 40}, {3, 50}, {5, 70} };
    int capacity = 4;
    int max_value = UnboundedKnapsack(items, capacity);

    std::cout << "得られる最大値(UnboundedKnapsack): " << max_value << std::endl;
}

こちらも0-1ナップザック問題とあまり変わらずに実装できました

最後に

今回はいつもと趣向の違った内容で記事にしてみましたが、こういうアルゴリズム的なものに挑戦するのも楽しいですね

-プログラム, 技術
-