This commit is contained in:
krahets
2026-04-03 18:46:15 +08:00
parent 377736b1bd
commit 9d21ca86b0
352 changed files with 46563 additions and 11262 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3,22 +3,22 @@ comments: true
icon: material/table-pivot
---
# 第 14 章   動的プログラミング
# 第 14 章   動的計画法
![動的プログラミング](../assets/covers/chapter_dynamic_programming.jpg){ class="cover-image" }
![動的計画法](../assets/covers/chapter_dynamic_programming.jpg){ class="cover-image" }
!!! abstract
川が流れて海に注ぐように、
動的プログラミングは小さな問題の解を織り合わせて、より大きな問題の解へと導きます。一歩一歩進んで、最終的な答えが待つ彼岸へと向かいます
小川は川へと注ぎ、河川は大海へと注ぐ。
動的計画法は小さな問題の解を集めて大きな問題の答えとし、一歩ずつ私たちを問題解決の彼岸へと導く
## 章の内容
- [14.1   動的計画法の初歩](intro_to_dynamic_programming.md)
- [14.2   DP 問題特性](dp_problem_features.md)
- [14.3   DP の解法の考え方](dp_solution_pipeline.md)
- [14.4   0-1ナップサック問題](knapsack_problem.md)
- [14.1   動的計画法入門](intro_to_dynamic_programming.md)
- [14.2   動的計画法の問題特性](dp_problem_features.md)
- [14.3   動的計画法の問題解決の考え方](dp_solution_pipeline.md)
- [14.4   0-1 ナップサック問題](knapsack_problem.md)
- [14.5   完全ナップサック問題](unbounded_knapsack_problem.md)
- [14.6   編集距離問題](edit_distance_problem.md)
- [14.7   まとめ](summary.md)

File diff suppressed because it is too large Load Diff

View File

@@ -4,24 +4,26 @@ comments: true
# 14.7   まとめ
- 動的プログラミングは問題を分解し、部分問題の解を保存することで冗長な計算を避け、計算効率を向上させます。
- 時間を考慮しなければ、すべての動的プログラミング問題はバックトラッキング(力任せ探索)を使用して解決できますが、再帰木には多くの重複する部分問題があり、効率が非常に低くなります。記憶化リストを導入することで、計算されたすべての部分問題の解を保存し、重複する部分問題が一度だけ計算されることを保証できます。
- 記憶化探索はトップダウンの再帰解法であり、動的プログラミングはボトムアップの反復アプローチに対応し、「表を埋める」ことに似ています。現在の状態は特定の局所状態のみに依存するため、dpテーブルの1次元を削除して空間計算量を削減できます。
- 部分問題の分解は汎用的なアルゴリズムアプローチであり、分割統治法、動的プログラミング、バックトラッキングで特徴が異なります。
- 動的プログラミング問題には3つの主要な特徴があります重複する部分問題、最適部分構造、無記憶性
- 元の問題の最適解がその部分問題の最適解から構築できる場合、最適部分構造を持ちます。
- 無記憶性とは、状態の将来の発展が現在の状態のみに依存し、過去に経験したすべての状態に依存しないことを意味します。多くの組み合わせ最適化問題にはこの特性がなく、動的プログラミングを使用して迅速に解決することはできません
### 1.   要点の振り返り
- 動的計画法は問題を分解し、部分問題の解を保存することで重複計算を避け、計算効率を高めます。
- 時間を考慮しなければ、すべての動的計画法の問題はバックトラッキング(総当たり探索)で解けますが、再帰木には大量の重複部分問題が存在するため、効率はきわめて低くなります。メモ化配列を導入すると、計算済みのすべての部分問題の解を保存でき、重複部分問題が 1 回だけ計算されることを保証できます。
- メモ化探索はトップダウンの再帰的解法であり、それに対応する動的計画法はボトムアップの漸化式による解法で、ちょうど「表を埋める」ようなものです。現在の状態は一部の局所状態にのみ依存するため、$dp$ 表の 1 次元を削減して空間計算量を下げることができます
- 部分問題への分解は汎用的なアルゴリズムの考え方であり、分割統治、動的計画法、バックトラッキングではそれぞれ異なる性質を持ちます。
- 動的計画法の問題には 3 つの大きな特徴があります。重複部分問題、最適部分構造、無後効性です
- 元の問題の最適解が部分問題の最適解から構築できるなら、その問題は最適部分構造を持ちます。
- 無後効性とは、ある状態の将来の発展がその状態のみに関係し、過去に経たすべての状態とは無関係であることを指します。多くの組合せ最適化問題は無後効性を持たず、動的計画法で高速に解くことはできません。
**ナップサック問題**
- ナップサック問題は最も典型的な動的プログラミング問題の1つで、0-1ナップサック、無制限ナップサック、複数ナップサックなどの変種があります。
- 0-1ナップサックの状態定義は、最初の $i$ 個のアイテムを含む容量 $c$ のナップサックでの最大値です。アイテムをナップサックに入れないまたは入れるという決定に基づいて、最適部分構造を特定し、状態遷移方程式を構築できます。空間最適化では、各状態が直接上と左上の状態に依存するため、左上の状態上書きを避けるためにリストを逆順走査する必要があります。
- 無制限ナップサック問題では、各種類のアイテムを選択できる数に制限がないため、アイテムを含める状態遷移は0-1ナップサック異なります。状態が直接上と左の状態に依存するため、空間最適化では前方走査を含める必要があります。
- コイン交換問題は無制限ナップサック問題の変種で「最大」値を求めることから「最小」コイン数を求めることに変わり、状態遷移方程式 $\max()$ $\min()$ に変更する必要があります。ナップサック容量を「超えない」ことを追求することから、正確に目標金額を求めることに変わり、「目標金額を構成できない」無効解を表すため$amt + 1$ を使用します。
- コイン交換問題IIは「最小コイン数」を求めることから「コインの組み合わせ数」を求めることに変わり、状態遷移方程式 $\min()$ から和演算子に変更します。
- ナップサック問題は最も典型的な動的計画法の問題の 1 つであり、0-1 ナップサック、完全ナップサック、多重ナップサックなどの派生があります。
- 0-1 ナップサックの状態は、容量 $c$ のナップサックに対して、前 $i$ 個の品物で得られる最大価値として定義されます。ナップサックに入れない場合と入れる場合の 2 つの判断から最適部分構造を得て、状態遷移方程式を構築できます。空間最適化では、各状態が上と左上の状態に依存するため、左上の状態上書きされるのを避けるために配列を逆順走査する必要があります。
- 完全ナップサック問題では各品物の選択数に制限がないため、品物を入れる場合の状態遷移は 0-1 ナップサック問題とは異なります。状態は真上と左の状態に依存するので、空間最適化では順方向に走査するべきです。
- コイン両替問題は完全ナップサック問題の変種です。「最大」値を求める問題から「最小」の硬貨枚数を求める問題へ変わるため、状態遷移方程式 $\max()$ $\min()$ に置き換える必要があります。また、ナップサック容量を「超えない」ことを目指すのではなく、目標金額を「ちょうど」作ることを目指すため$amt + 1$ を「目標金額を作れない」無効解の表現として用います。
- コイン両替問題 II では、「最少硬貨枚数」を求める問題から「硬貨の組合せ数」を求める問題へ変わるため、状態遷移方程式 $\min()$ から和演算子へ対応して変わります。
**編集距離問題**
- 編集距離(レーベンシュタイン距離)は2つの文字列間の類似度を測定し、一つの文字列を別の文字列に変更するために必要な最小編集ステップ数として定義され編集操作には追加、削除、置換が含まれます。
- 編集距離問題の状態定義は、$s$ の最初の $i$ 文字を $t$ の最初の $j$ 文字変更するために必要な最小編集ステップ数です。$s[i] \ne t[j]$ の場合、追加、削除、置換の3つの決定があり、それぞれに対応する残部分問題があります。これから最適部分構造を特定し、状態遷移方程式を構築できます。$s[i] = t[j]$ の場合、現在の文字編集必要ありません。
- 編集距離では、状態が直接上、左、左上の状態に依存します。したがって、空間最適化後、前方走査も逆走査も正しく状態遷移を実行できません。これに対処するため、変数を使用して左上の状態を一時的に保存し、無制限ナップサック問題の状況と同等にし、空間最適化後に前方走査を可能にします。
- 編集距離(Levenshtein 距離)は 2 つの文字列間の類似度を測るために用いられ、ある文字列を別の文字列へ変換するための最小編集回数として定義されます。編集操作には追加、削除、置換が含まれます。
- 編集距離問題の状態は、$s$ の $i$ 文字を $t$ の $j$ 文字変更するに必要な最小編集回数として定義されます。$s[i] \ne t[j]$ のときは、追加、削除、置換の 3 つの判断があり、それぞれに対応する残りの部分問題があります。これにより最適部分構造を見いだし、状態遷移方程式を構築できます。一方、$s[i] = t[j]$ のときは現在の文字編集する必要ありません。
- 編集距離では、状態は真上、左、左上の状態に依存します。そのため、空間最適化後は順方向でも逆方向でも正しく状態遷移できません。そこで、変数を 1 つ用いて左上の状態を一時保存し、完全ナップサック問題と等価な形へ変換することで、空間最適化後に順方向走査を行えるようにします。