Re-translate the Japanese version (#1871)

* Retranslate Japanese docs with GPT-5.4

* Retranslate Japanese code with GPT-5.4
This commit is contained in:
Yudong Jin
2026-03-30 07:30:15 +08:00
committed by GitHub
parent fe6443235b
commit d7b2277d2b
1444 changed files with 83312 additions and 8363 deletions
@@ -1,65 +1,65 @@
# 動的プログラミング問題解決アプローチ
# 動的計画法の問題解決の考え方
前の2つのセクションでは動的プログラミング問題の主要な特徴を紹介しました。次に、より実用的な2つの問題を一緒に探索しましょう
前の 2 節では動的計画法の問題の主要な特徴を紹介しました。ここからは、さらに実用的な 2 つの問題を一緒に考えていきます
1. 問題が動的プログラミング問題かどうかをどのように判断すか?
2. 動的プログラミング問題を解決する完全なステップは何か?
1. ある問題が動的計画法の問題かどうかをどのように判断すればよいでしょうか?
2. 動的計画法の問題を解くには、どこから着手し、完全な手順はどのようなものでしょうか?
## 問題の判定
一般的に言えば、問題が重複する部分問題最適部分構造を含み、無記憶性を示す場合、通常動的プログラミング解法に適しています。しかし、問題の説明から直接これらの特徴を抽出することはしばしば困難です。したがって、通常は条件を緩和し、**まず問題がバックトラッキング(全探索)を使用した解決に適しているかどうかを観察**します。
一般に、ある問題が重複部分問題最適部分構造を含み、さらに無後效性を満たしているなら、通常動的計画法で解くのに適しています。しかし、問題文からこれらの性質を直接読み取るのは簡単ではありません。そのため通常は条件を少し緩めて、**まずその問題がバックトラッキング(全探索)で解くのに適しているか**を観察します。
**バックトラッキングに適した問題は通常「決定木モデル」に適合**し、これは木構造を使用して記述でき、各ノードは決定を表し、各パスは決定のシーケンスを表します。
**バックトラッキングで解くのに適した問題は通常「決定木モデル」を満たします**。この種の問題は木構造で表現でき、各ノードは 1 つの決定を表し、各経路は 1 つの決定列を表します。
言い換えると、問題が明示的な決定概念を含み、解が一連の決定を通じて生成される場合、それは決定木モデルに適合し、通常バックトラッキングを使用して解決できます。
言い換えると、問題に明確な決定概念が含まれており、解が一連の決定によって生成されるなら、その問題は決定木モデルを満たし、通常バックトラッキングで解くことができます。
の基礎の上で、動的プログラミング問題を判定するための「ボーナスポイント」があります。
れに加えて、動的計画法の問題には判定のための「加点要素」もあります。
- 問題に最大(最小化)または最も(最も少ない)最適な解を見つけるという記述が含まれている。
- 問題の状態がリスト、多次元行列、または木を使用して表現でき、状態その周の状態と再帰関係を持っている。
- 問題に最大(最小)や最多(最少)などの最適化に関する記述がある。
- 問題の状態が配列、多次元行列、または木表現でき、ある状態その周の状態の間に漸化的な関係がある。
対応して、「ペナルティポイント」もあります。
反対に、「減点要素」もあります。
- 問題の目標は最適解だけでなく、すべての可能な解を見つけることである。
- 問題の説明に順列と組み合わせの明らかな特徴があり、特定の複数の解を返す必要がある。
- 問題の目的が最適解を求めることではなく、あり得るすべての解を列挙することである。
- 問題文に明確な順列・組合せの特徴があり、具体的な複数の解を返す必要がある。
問題が決定木モデルに適合し、比較的明らかな「ボーナスポイント」を持つ場合、それが動的プログラミング問題であると仮定し、解決プロセス中に検証できます。
ある問題が決定木モデルを満たし、さらに比較的明確な「加点要素」を備えているなら、その問題は動的計画法の問題であると仮定し、解く過程でそれを検証できます。
## 問題解決ステップ
## 問題を解く手順
動的プログラミング問題解決プロセスは問題の性質難易度によって異なりますが、一般的に次のステップに従います:決定記述、状態定義、$dp$ テーブルの確立、状態遷移方程式導出、境界条件の決定など
動的計画法の解法の流れは問題の性質難易度によって異なりますが、通常は次の手順に従います。すなわち、決定記述、状態定義、$dp$ テーブルを構築し、状態遷移方程式導出、境界条件を定めます
問題解決ステップをより具体的に説明するために、古典的な問題「最小経路和」を例として使用します。
解法の手順をより具体的に示すために、ここでは古典的な問題である「最小経路和」を例します。
!!! question
$n \times m$ の次元グリッド `grid` が与えられグリッドの各セルには負でない整数が含まれ、そのセルのコストを表します。ロボットは左上のセルから始まり、各ステップで下または右にのみ移動でき、右下のセルに到達するまで続けます。左上から右下の最小経路和を返してください。
$n \times m$ の 2 次元グリッド `grid` が与えられます。グリッドの各セルには非負整数が格納されており、そのセルのコストを表します。ロボットは左上のセルを始点とし、毎回下または右に 1 マスだけ移動して、右下のセルまで進みます。左上から右下までの最小経路和を返してください。
の図は例を示しており、与えられたグリッドの最小経路和は $13$ です。
の図は 1 つの例を示しており、このグリッドの最小経路和は $13$ です。
![最小経路和のデータ](dp_solution_pipeline.assets/min_path_sum_example.png)
![最小経路和のサンプルデータ](dp_solution_pipeline.assets/min_path_sum_example.png)
**第1ステップ:各ラウンドの決定を考え、状態を定義し、それにより $dp$ テーブルを得る**
**ステップ 1:各ラウンドの決定を考え、状態を定義し、$dp$ テーブルを得る**
この問題各ラウンドの決定は、現在のセルから下または右に1ステップ移動することです。現在のセルの行と列のインデックス $[i, j]$ であると仮定すると、下または右に移動した後、インデックスは $[i+1, j]$ または $[i, j+1]$ になります。したがって、状態には2つの変数が含まれるべきです:行インデックスと列インデックス、$[i, j]$ と表記されます。
この問題における各ラウンドの決定は、現在のマスから下または右へ 1 マス進むことです。現在のマスの行・列インデックス $[i, j]$ すると、下または右へ 1 マス進んだ後のインデックスは $[i+1, j]$ または $[i, j+1]$ になります。したがって、状態には行インデックスと列インデックスの 2 つの変数を含め、$[i, j]$ と表ます。
状態 $[i, j]$ は部分問題に対応します:開始点 $[0, 0]$ から $[i, j]$ への最小経路和$dp[i, j]$ と表記されます。
状態 $[i, j]$ に対応する部分問題は、始点 $[0, 0]$ から $[i, j]$ まで進む最小経路和であり、その解を $dp[i, j]$ と記します。
のようにして、下の図に示す次元 $dp$ 行列を得ます。そのサイズは入力グリッド $grid$ と同じです。
れで、次の図に示す 2 次元 $dp$ 行列が得られます。そのサイズは入力グリッド $grid$ と同じです。
![状態定義とDPテーブル](dp_solution_pipeline.assets/min_path_sum_solution_state_definition.png)
![状態定義と dp テーブル](dp_solution_pipeline.assets/min_path_sum_solution_state_definition.png)
!!! note
動的プログラミングとバックトラッキングは決定のシーケンスとして記述でき状態はすべての決定変数から構成されます。問題解決の進行を記述するすべての変数を含むべきで、次の状態を導出するのに十分な情報を含んでいる必要があります。
動的計画法とバックトラッキングの過程は、いずれも 1 つの決定列として記述できます。そして状態はすべての決定変数から構成されます。状態には解法の進行状況を表すすべての変数が含まれているべきであり、次の状態を導のに十分な情報を持っている必要があります。
各状態は 1 つの部分問題に対応しており、すべての部分問題の解を保存するために $dp$ テーブルを定義します。状態の各独立変数は、$dp$ テーブルの 1 つの次元に対応します。本質的に、$dp$ テーブルは状態と部分問題の解との対応関係です。
各状態は部分問題に対応し、すべての部分問題の解を保存するための $dp$ テーブルを定義します。状態の各独立変数は $dp$ テーブルの次元です。本質的に、$dp$ テーブルは状態と部分問題の解の間のマッピングです。
**ステップ 2:最適部分構造を見つけ、状態遷移方程式を導出する**
**第2ステップ:最適部分構造を特定し、状態遷移方程式を導出する**
状態 $[i, j]$ は、上のマス $[i-1, j]$ または左のマス $[i, j-1]$ からしか遷移してきません。したがって最適部分構造は、$[i, j]$ に到達する最小経路和が、$[i, j-1]$ の最小経路和と $[i-1, j]$ の最小経路和のうち小さい方によって決まる、ということです。
状態 $[i, j]$ について、それは上のセル $[i-1, j]$ または左のセル $[i, j-1]$ からのみ導出できます。したがって、最適部分構造は:$[i, j]$ に到達する最小経路和は、$[i, j-1]$ と $[i-1, j]$ の最小経路和の小さい方によって決定されます。
上記の分析に基づいて、下の図に示す状態遷移方程式を導出できます:
以上の分析から、次の図に示す状態遷移方程式を導くことができます。
$$
dp[i, j] = \min(dp[i-1, j], dp[i, j-1]) + grid[i, j]
@@ -69,75 +69,75 @@ $$
!!! note
定義された $dp$ テーブルに基づいて、元の問題と部分問題の関係を考え、部分問題の最適解から元の問題の最適解をどのように構築するか、つまり最適部分構造を見つけます。
定義済みの $dp$ テーブルに基づいて、元の問題と部分問題の関係を考え、部分問題の最適解から元の問題の最適解を構成する方法、すなわち最適部分構造を見つけます。
最適部分構造を特定したら、それを使用して状態遷移方程式を構築できます。
ひとたび最適部分構造が見つかれば、それを使て状態遷移方程式を構築できます。
**第3ステップ:境界条件と状態遷移順序を決定す**
**ステップ 3:境界条件と状態遷移順序を決**
この問題では、最初の行の状態は左の状態からのみ来ることができ、最初の列の状態は上の状態からのみ来ることができるため、最初の行 $i = 0$ と最初の列 $j = 0$ が境界条件す。
この問題では、先頭行にある状態は左の状態からしか得られず、先頭列にある状態は上の状態からしか得られません。したがって、先頭行 $i = 0$ と先頭列 $j = 0$ が境界条件になります。
の図に示すように、各セルは左のセルと上のセルから導出されるため、ループを使用して行列を走査し外側のループは行を反復し、内側のループは列を反復します。
の図に示すように、各マスは左のマスと上のマスから遷移してくるため、ループを用いて行列を走査します。外側のループで各行を、内側のループで各列を走査します。
![境界条件と状態遷移順序](dp_solution_pipeline.assets/min_path_sum_solution_initial_state.png)
![境界条件と状態遷移順序](dp_solution_pipeline.assets/min_path_sum_solution_initial_state.png)
!!! note
境界条件は動的プログラミングで $dp$ テーブル初期化するために使用され、探索では枝刈りに使用されます。
境界条件は動的計画法では $dp$ テーブル初期化に使われ、探索では枝刈りに使れます。
状態遷移の順序で重要なのは、現在の問題の解を計算するときに、それが依存するより小さな部分問題の解がすべてすでに正しく計算済みであることを保証する点です。
状態遷移順序の核心は、現在の問題の解を計算するとき、それが依存するすべての小さな部分問題が既に正しく計算されていることを確保することです。
以上の分析により、すでに動的計画法のコードを直接書くことができます。しかし、部分問題への分解はトップダウンの考え方であるため、「力任せ探索 $\rightarrow$ メモ化探索 $\rightarrow$ 動的計画法」の順に実装するほうが、思考の流れにはより自然です。
上記の分析に基づいて、動的プログラミングコードを直接書くことができます。しかし、部分問題の分解はトップダウンアプローチであるため、「力任せ探索 → メモ化探索 → 動的プログラミング」の順序で実装することが習慣的な思考により適合します。
### 方法 1:力任せ探索
### 方法1:力任せ探索
状態 $[i, j]$ から探索を開始し、より小さな状態 $[i-1, j]$ と $[i, j-1]$ へと分解していきます。再帰関数には次の要素が含まれます。
状態 $[i, j]$ から探索を開始し、それを常により小さな状態 $[i-1, j]$ と $[i, j-1]$ に分解します。再帰関数には以下の要素が含まれます
- **再帰パラメータ**:状態 $[i, j]$。
- **戻り値**$[0, 0]$ から $[i, j]$ への最小経路和 $dp[i, j]$。
- **再帰引数**状態 $[i, j]$ 。
- **戻り値**$[0, 0]$ から $[i, j]$ までの最小経路和 $dp[i, j]$ 。
- **終了条件**$i = 0$ かつ $j = 0$ のとき、コスト $grid[0, 0]$ を返す。
- **枝刈り**$i < 0$ または $j < 0$ でインデックスが範囲外のとき、コスト $+\infty$ を返し、実行不可能を表す。
- **枝刈り**$i < 0$ または $j < 0$ でインデックスが範囲外になった場合、コスト $+\infty$ を返し、実行不可能であることを表す。
実装コードは以下の通りです
実装コードは次のとおりです
```src
[file]{min_path_sum}-[class]{}-[func]{min_path_sum_dfs}
```
の図は $dp[2, 1]$ を根とする再帰木を示しており、いくつかの重複する部分問題を含み、その数はグリッド `grid` のサイズが増加すると急激に増加します。
の図は$dp[2, 1]$ を根ノードとする再帰木を示しています。この中にはいくつかの重複部分問題が含まれており、その数はグリッド `grid` のサイズが大きくなるにつれて急激に増加します。
本質的に、重複する部分問題理由は**左上から特定のセル到達する複数のパスが存在する**ことです。
本質的に、重複部分問題が生じる理由は**左上からあるセル到達する経路が複数存在すること**にあります。
![力任せ探索の再帰木](dp_solution_pipeline.assets/min_path_sum_dfs.png)
各状態には下と右の2つの選択があるため、左上から右下までの総ステップ数は $m + n - 2$ で、最悪時間計算量は $O(2^{m + n})$ です。この計算方法はグリッドエッジ近くの状況を考慮していないことに注意してください。ネットワークエッジに到達したとき、選択肢が1つしか残らないため、実際のパス数はより少なくなります。
各状態には下と右の 2 通りの選択があ、左上から右下まで進むには合計で $m + n - 2$ 歩必要です。したがって最悪時間計算量は $O(2^{m + n})$ です。ここで、$n$ と $m$ はそれぞれグリッドの行数と列数を表します。なお、この見積もりではグリッド境界付近の状況を考慮していません。境界に達すると選択肢は 1 つだけになるため、実際の経路数はこれより少なくなります。
### 方法2:メモ化探索
### 方法 2:メモ化探索
グリッド `grid` と同じサイズのメモリスト `mem` を導入し、様々な部分問題の解を記録し、重複する部分問題を枝刈りします
グリッド `grid` と同じサイズのメモ配列 `mem` を導入し、部分問題の解を記録し、重複部分問題を枝刈りします
```src
[file]{min_path_sum}-[class]{}-[func]{min_path_sum_dfs_mem}
```
の図に示すように、メモ化を導入した後、すべての部分問題の解は一度だけ計算される必要があるため、時間計算量は状態総数、つまりグリッドサイズ $O(nm)$ に依存します。
の図に示すように、メモ化を導入すると、すべての部分問題の解は 1 回だけ計算すればよくなります。したがって時間計算量は状態総数、すなわちグリッドサイズ $O(nm)$ に依存します。
![メモ化探索の再帰木](dp_solution_pipeline.assets/min_path_sum_dfs_mem.png)
### 方法3:動的プログラミング
### 方法 3:動的計画法
動的プログラミング解法を反復的に実装します。コードは以下の通りです:
反復に基づいて動的計画法の解法を実装すると、コードは次のようになります。
```src
[file]{min_path_sum}-[class]{}-[func]{min_path_sum_dp}
```
の図は最小経路和の状態遷移プロセスを示し、グリッド全体を走査するため、**時間計算量は $O(nm)$** です。
の図は最小経路和の状態遷移の過程を示しています。グリッド全体を走査するため、**時間計算量は $O(nm)$** です。
配列 `dp` のサイズは $n \times m$ であるため、**空間計算量は $O(nm)$** です。
=== "<1>"
![最小経路和の動的プログラミングプロセス](dp_solution_pipeline.assets/min_path_sum_dp_step1.png)
![最小経路和の動的計画法の過程](dp_solution_pipeline.assets/min_path_sum_dp_step1.png)
=== "<2>"
![min_path_sum_dp_step2](dp_solution_pipeline.assets/min_path_sum_dp_step2.png)
@@ -174,9 +174,9 @@ $$
### 空間最適化
セルは左と上のセルのみに関連するため、単一行配列を使用して $dp$ テーブルを実装できます。
マスは左のマスと上のマスにのみ関係するため、1 行の配列だけを使って $dp$ テーブルを実装できます。
配列 `dp`1行の状態のみを表現できるため、最初の列の状態を事前に初期化できず、各行を走査するときに更新することに注意してください:
ただし、配列 `dp` 1 行分の状態しか表せないため、先頭列の状態を事前に初期化することはできず、各行を走査するときに更新する必要があります。
```src
[file]{min_path_sum}-[class]{}-[func]{min_path_sum_dp_comp}