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