* Retranslate the complete Japanese online edition from Chinese * Fit Japanese homepage labels and localize heading permalinks * Point Japanese contribution instructions to the Japanese preview * Replace locale conditionals with multilingual UI tables
7.0 KiB
練習問題
知識の確認
動的計画法に適した場面
ある生徒が「漸化式を書けるなら、必ず動的計画法を使うべきだ」と言いました。
次の各作業には、動的計画法、バックトラッキング、または dp 表を作らずループや数式を使う方法のどれがより適しているかを判断し、主な理由を一つ書いてください。
-
額面
[1, 3, 4]の硬貨で金額 6 を作るための最少枚数を求めます。各硬貨は何枚でも使えます。 -
[1, 2, 3]の全順列を出力します。 -
1 + 2 + \dots + nを計算します。動的計画法に適していると判断した作業については、
dp[i]が何を表すかも説明してください。
??? success "解答例"
1. 動的計画法に適しています。`dp[i]` を、金額 `i` を作るために必要な硬貨の最少枚数とします。
`i` 以下の額面の各硬貨 `c` について `dp[i-c] + 1` を答えの候補とし、
その最小値を取ります。異なる選択でも同じ金額に繰り返し行き着き、大きな金額の最適解も小さな金額の最適解から構成できます。
金額 6 の答えは、`3 + 3` の 2 枚です。
2. バックトラッキングを使います。求められるのは 6 個すべての順列を一つずつ生成することであり、バックトラッキングなら、一つを選んで探索を進め、
選択を取り消して別の分岐を試す作業を体系的に行えます。どの方法でも、実際に順列を出力するときには列挙を省略できません。
3. ループまたは等差数列の公式で十分です。`S(i) = S(i-1) + i` と書けますが、`S(i)` は一つの小さな `S(i-1)` だけに依存し、
各部分和は一回計算すれば済みます。重複する部分問題がないため、`dp` 表は不要です。「漸化式を書ける」ことと「動的計画法が必要」なことは同じではありません。
ナップサックの表の一マスを計算する
0-1 ナップサック問題で、品物の重さは wgt = [1, 2, 3]、価値は val = [5, 11, 15]、容量は 4 です。
dp[i][c] は、最初の i 個の品物だけを考え、容量の上限を c としたときに得られる最大の価値を表します。
ナップサックをちょうど満杯にする必要はありません。
状態 dp[3][4] だけを計算します。dp[2][4] = 16、dp[2][1] = 5 は分かっているものとします。
- 3 番目の品物を選ばない場合、価値の候補はいくつですか。
- 3 番目の品物を選ぶ場合、残りの容量と価値の候補はいくつですか。
dp[3][4]はいくつになりますか。どの品物を選ぶことに対応しますか。
??? success "解答例"
1. 3 番目の品物を選ばない場合は、最初の二つの品物の結果を使い、候補は `dp[2][4] = 16` です。
2. 3 番目の品物の重さは 3 なので、入れた後の残りの容量は $4-3=1$ です。価値の候補は
`dp[2][1] + 15 = 5 + 15 = 20` です。
3. 16 と 20 を比較して、`dp[3][4] = 20` となります。これは 1 番目と 3 番目の品物を選ぶことに対応し、
重さの合計は $1+3=4$、価値の合計は $5+15=20$ です。
この状態の計算は、0-1 ナップサック問題での一回の「選ぶか選ばないか」の比較を表しています。
ナップサックの容量を更新する順序
0-1 ナップサック問題に、重さ 2、価値 5 の品物が一つだけあります。ナップサックの容量は 4 です。
各品物は一回だけ選べ、初期の一次元配列は dp = [0, 0, 0, 0, 0] です。
ある生徒がこの品物を処理するとき、容量 2 から 4 の順に更新しました。
dp[2]を更新して 5 を得ました。dp[3]を更新して、ここでも 5 を得ました。dp[4]の更新では、今求めたdp[2]を使ったため、dp[4] = 10となりました。
dp[4] = 10は正しい結果ですか。理由も説明してください。- 「各品物は一回だけ選べる」という条件から、
dp[4]はいくつになるべきですか。 - 各品物を処理するとき、容量は大きい順と小さい順のどちらで更新すべきですか。その順序によって、どの問題を避けられますか。
??? success "解答例"
1. 正しくありません。価値 10 は、価値 5 の同じ品物を二回入れたことに相当し、
「各品物は一回だけ選べる」という条件に違反します。
2. 入れられるのはこの一つの品物だけなので、正しい `dp[4]` は 5 です。
3. 容量は大きい順、つまり 4、3、2 の順に更新します。
そうすれば、`dp[c]` を計算するときに読み取る `dp[c-2]` は、まだ現在の品物を処理する前の結果なので、
同じ反復内で現在の品物を重複して使わずに済みます。
プログラミング演習
階段の上り方の数
n 段の階段があります。一回に 1 段か 2 段だけ上れ、ちょうど第 n 段に到達する必要があります。
異なる上り方が何通りあるかを計算してください。n >= 1 とし、各歩で 1 段進むか 2 段進むかによってのみ上り方を区別します。
一次元の動的計画法の配列を使い、二つの状態だけを残す空間最適化は、ここでは行わないでください。
??? tip "解くためのヒント"
1. 第 i 段に到達する最後の一歩は、1 段または 2 段しか進めません
2. したがって、dp[i] = dp[i-1] + dp[i-2] が成り立ちます
3. まず n が 1 と 2 の場合を処理し、第 3 段から表を埋めます
LeetCode{ .rounded-button .exercise-button target="_blank" rel="noopener noreferrer" }
0-1 ナップサック問題
同じ長さの配列 wgt と val が与えられます。品物 i の重さは正の整数 wgt[i]、価値は非負整数 val[i] です。
ナップサックの容量 cap は非負整数です。各品物は一回だけ選べます。重さの合計が cap を超えない範囲で、
ナップサックに入れられる価値の合計の最大値を求めてください。一次元の動的計画法で実装してください。
??? tip "解くためのヒント"
1. 長さ cap + 1 の配列 dp を初期化します。dp[c] は、容量の上限が c の場合の最大の価値を表します
2. 品物 i を処理するときは、「選ばない」場合の dp[c] と、「選ぶ」場合の dp[c-wgt[i]] + val[i] を比較します
3. 同じ反復内で現在の品物を重複して選ばないよう、容量は必ず大きい順に更新します