動的プログラミング - 0/1 ナップザック問題

1. 問題の説明

0/1 ナップザック問題 1
制限時間:  1000MS メモリ制限:  5000 KB

説明

C (C<=100) と N (N<=500) 個の宝石の容量を持つバックパックがあり、i 番目の宝石のサイズは si、値は vi です。条件が限られているため、
宝石を運ぶための唯一のツールとしてこのバックパックを手元に置く必要があります。ここで、最大でどれくらいの価値の宝石を持ち帰ることができるかを知りたいと思います。

入力

最初の行の入力 M (M<=10) は、M セットのデータがあることを示します。データの各セットの最初の行に N と C を入力して、ジェムの数とナップザックの容量を示します。次の行に
N セット (si, vi) を入力します。si と vi は両方とも整数で、サイズを示します。そしてそれぞれの宝石の価値。

出力

正の整数を M 行出力します。i 番目の行は、i 番目のデータグループによって取り出せる宝石の最大コストを表し、バックパックがいっぱいである必要はありません。

サンプル入力

3 
3 10 
1 3 2 5 7 2 
3 10 
1 3 2 5 6 2 5 
10 
5 6 5 7 2 8 8 1 5 9

サンプル出力

10 
10 
17

2. 問題分析

この問題は動的計画法を使用して解決する必要があることに疑いの余地はありません。

dp 配列を定義します。ここで、 dp[i][j] は、容量 j のバックパックに最初の i 個のアイテムを入れることで取得できる最大値を表します。i 番目のアイテムについては、バックパックに入れるか入れないかを考慮できるため、次の状態遷移方程式が得られます。

dp[i][j] = max(dp[i-1][j], dp[i-1][j-si]+vi)

ここで、si と vi はそれぞれ i 番目の項目のサイズと値を表します。i 番目のアイテムをバックパックに入れた場合、それが占める容量は j-si であり、このときに取得できる最大値は dp[i-1][j-si]+vi、つまり、最初の i- 1 j-si に i 番目のアイテムの値を加えた容量のアイテムをナップザックに入れることで得られる最大値。i 番目のアイテムをバックパックに入れていない場合、このときに取得できる最大値は dp[i-1][j]、つまり最初の i-1 を入れることで得られる値になります。最大値 j の容量のバックパックにアイテムを入れます。

最終的な答えは dp[N][C] で、これは容量 C のバックパックに N 個のアイテムを入れたときに得られる最大値です。

3. コード例

using namespace std;

const int MAXN = 505;
const int MAXC = 105;

int s[MAXN], v[MAXN];
int dp[MAXC];

int main() {
    int M;
    cin >> M;
    while (M--) {
        int N, C;
        cin >> N >> C;
        for (int i = 1; i <= N; i++) {
            cin >> s[i] >> v[i];
        }

        for (int i = 0; i < C + 1; i++) {
            dp[i] = 0;
       }
        for (int i = 1; i <= N; i++) {
            for (int j = C; j >= s[i]; j--) {
                dp[j] = max(dp[j], dp[j - s[i]] + v[i]);
            }
        }

        cout << dp[C] << endl;
    }
    return 0;
}

解説:for (int i = 1; i <= N; i++) {             for (int j = C; j >= s[i]; j--) {                 dp[j] = max(dp[j], dp [j - s[i]] + v[i]);             }         }



上記のコードは、動的計画法の考え方を使用した、0/1 ナップザック問題の核心部分です。具体的には、2 次元配列 dp[i][j] を使用して、ナップザックの容量 j の制限の下で最初の i 個の宝石が取得できる最大値を表します。前の行の情報のみが必要なため、2 次元配列を 1 次元配列 dp[j] に最適化できます。

次に、各宝石を順番に検討します。各宝石について、パッティングする場合とパッティングしない場合の両方を考慮する必要があります。現在の gem を入れない場合、ナップザック容量 j の制限の下で取得できる最大値は dp[j] であり、現在の gem を入れた場合、制限の下で取得できる最大値は dp[j] です。ナップザック容量 j の値は dp[js[i]]+v[i] です。ここで、s[i] は現在の gem のサイズを表し、v[i] は現在の gem の値を表します。現在の gem を入れることができるのは 1 回だけであるため、dp 配列を更新するときに、max 関数を使用して、現在の gem を入れるか入れないかによって取得できる最大値を選択する必要があります。

j を後ろから前にたどる必要があるため、最終的な答えは dp[C] になります。

おすすめ

転載: blog.csdn.net/lyhizjj/article/details/130624032