Skip to content

Commit d366816

Browse files
committed
leetcode 91. Decode Ways ローリングDP
1 parent 0458e53 commit d366816

9 files changed

Lines changed: 3701 additions & 28 deletions

File tree

‎.markdownlint.json‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@
66
"tables": false
77
},
88
"MD033": {
9-
"allowed_elements": ["h2", "details", "summary", "p", "i", "footer", "div"]
9+
"allowed_elements": [
10+
"h1",
11+
"h2",
12+
"details",
13+
"summary",
14+
"p",
15+
"i",
16+
"footer",
17+
"div"
18+
]
1019
}
1120
}

‎Algorithm/DynamicProgramming/README.md‎

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
## **動的計画法(Dynamic Programming, DP)とは?**
2-
動的計画法(DP)は、**「最適部分構造」** と **「部分問題の重複」** という性質を持つ問題を効率的に解くためのアルゴリズム設計手法です。
1+
# **動的計画法(Dynamic Programming, DP)とは?**
2+
3+
動的計画法(DP)は、**「最適部分構造」** と **「部分問題の重複」** という性質を持つ問題を効率的に解くためのアルゴリズム設計手法です。
34
具体的には、**問題を小さな部分問題に分解し、それらを再利用しながら解を構築する** 方法です。
45

56
---
67

78
## **動的計画法の基本的な考え方**
8-
DPを使う際には、以下の3つのステップを踏むのが一般的です。
9+
10+
DP を使う際には、以下の 3 つのステップを踏むのが一般的です。
911

1012
1. **状態の定義(`dp[i]` を何とするか)**
1113
2. **遷移式(状態の更新方法)を考える**
@@ -14,103 +16,124 @@ DPを使う際には、以下の3つのステップを踏むのが一般的で
1416

1517
---
1618

17-
## **DPの適用条件**
18-
動的計画法が適用できる問題には、次の2つの特徴があります。
19+
## **DP の適用条件**
20+
21+
動的計画法が適用できる問題には、次の 2 つの特徴があります。
1922

2023
### **① 最適部分構造(Optimal Substructure)**
24+
2125
> **「大きな問題の最適解が、小さな部分問題の最適解を使って表現できる」**
26+
2227
- 例:`dp[i]`(部屋 `i` への最短時間)は、`dp[i-1]` や `dp[i-2]` の値を使って求めることができる。
2328

2429
### **② 部分問題の重複(Overlapping Sub problems)**
30+
2531
> **「同じ部分問題を何度も計算する」**
32+
2633
- 例:`dp[i]` を求めるとき、`dp[i-1]` や `dp[i-2]` を参照するが、`dp[i-1]` も `dp[i-2]` を参照しているため、計算が重複する。
2734

2835
このような場合、**メモ化(配列に値を保存して使い回す)** することで、無駄な計算を省略し、高速化できる。
2936

3037
---
3138

32-
## **DPの分類**
33-
DPにはいくつかの種類があります。よく使われるものを紹介します。
39+
## **DP の分類**
40+
41+
DP にはいくつかの種類があります。よく使われるものを紹介します。
42+
43+
### **① 一次元 DP(配列`dp`を用いる)**
3444

35-
### **① 一次元DP(配列`dp`を用いる)**
3645
- **例:最短経路、階段の登り方、ナップザック問題**
3746
- **特徴**:
3847
- `dp[i]` という配列を用意して、ある状態から次の状態へ遷移していく。
3948
- 線形 (`O(N)`) の計算量で解けることが多い。
4049

4150
**例:今回のダンジョン問題**
51+
4252
```javascript
43-
dp[i] = min(dp[i-1] + A[i], dp[i-2] + B[i])
53+
dp[i] = min(dp[i - 1] + A[i], dp[i - 2] + B[i]);
4454
```
4555

46-
### **② 二次元DP(`dp[i][j]` のように表を用いる)**
56+
### **② 二次元 DP(`dp[i][j]` のように表を用いる)**
57+
4758
- **例:部分和問題、最長共通部分列(LCS)、動的計画法での迷路探索**
4859
- **特徴**:
49-
- `dp[i][j]` のような表を使い、2次元的に状態を管理する。
60+
- `dp[i][j]` のような表を使い、2 次元的に状態を管理する。
5061
- 計算量は `O(N*M)` になることが多い。
5162

5263
**例:最長共通部分列(LCS)**
64+
5365
```javascript
5466
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1], dp[i-1][j-1] + (文字が一致するか?))
5567
```
5668
57-
### **③ ナップザックDP(制約付きDP)**
58-
- **例:0-1ナップザック問題**
69+
### **③ ナップザック DP(制約付き DP)**
70+
71+
- **例:0-1 ナップザック問題**
5972
- **特徴**:
6073
- ある制約(例:重さ `W` を超えないようにする)を満たしつつ最適解を求める。
6174
- `dp[i][w]` を使うことが多い。
6275
6376
**例:ナップザック問題**
77+
6478
```javascript
65-
dp[i][w] = max(dp[i-1][w], dp[i-1][w - weight[i]] + value[i])
79+
dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - weight[i]] + value[i]);
6680
```
6781
6882
---
6983
7084
## **ダンジョン問題を動的計画法で解く流れ**
85+
7186
### **1. `dp[i]` の定義**
87+
7288
`dp[i]` を「部屋 `i` にたどり着くまでの最短時間」と定義。
7389
7490
### **2. 遷移式を考える**
75-
部屋 `i` へは、以下の2つの方法で移動できる:
91+
92+
部屋 `i` へは、以下の 2 つの方法で移動できる:
93+
7694
- **部屋 `i-1` から `i` へ**(時間 `A[i]` )
7795
- **部屋 `i-2` から `i` へ**(時間 `B[i]` )
7896
7997
したがって、遷移式は:
98+
8099
```javascript
81-
dp[i] = min(dp[i-1] + A[i-2], dp[i-2] + B[i-3])
100+
dp[i] = min(dp[i - 1] + A[i - 2], dp[i - 2] + B[i - 3]);
82101
```
83102
84103
### **3. 初期条件**
104+
85105
- `dp[1] = 0` (スタート地点)
86106
- `dp[2] = A[0]`(部屋 `2` へは `A[2]` で移動)
87107
88108
### **4. 実装**
109+
89110
```javascript
90111
function minTimeToReachRoomN(N, A, B) {
91-
let dp = new Array(N + 1).fill(Infinity);
92-
dp[1] = 0;
93-
94-
for (let i = 2; i <= N; i++) {
95-
dp[i] = Math.min(dp[i], dp[i - 1] + A[i - 2]);
96-
if (i > 2) {
97-
dp[i] = Math.min(dp[i], dp[i - 2] + B[i - 3]);
98-
}
112+
let dp = new Array(N + 1).fill(Infinity);
113+
dp[1] = 0;
114+
115+
for (let i = 2; i <= N; i++) {
116+
dp[i] = Math.min(dp[i], dp[i - 1] + A[i - 2]);
117+
if (i > 2) {
118+
dp[i] = Math.min(dp[i], dp[i - 2] + B[i - 3]);
99119
}
100-
return dp[N];
120+
}
121+
return dp[N];
101122
}
102123
```
103124
104125
---
105126
106127
## **動的計画法(DP)のメリット**
107-
✅ **計算の重複を避けて高速化できる**(`O(N)` や `O(N*M)` の計算量)
108-
✅ **最適解を求めるのに向いている**(最短経路や最大値・最小値を求める問題)
128+
129+
✅ **計算の重複を避けて高速化できる**(`O(N)` や `O(N*M)` の計算量)
130+
✅ **最適解を求めるのに向いている**(最短経路や最大値・最小値を求める問題)
109131
✅ **再帰よりも効率的な場合が多い**(特にメモ化なしの再帰は `O(2^N)` の計算量になることが多い)
110132
111133
---
112134
113135
## **まとめ**
136+
114137
- **動的計画法(DP)** は、「部分問題の重複」と「最適部分構造」を利用して問題を効率よく解く手法。
115138
- **ダンジョン問題** では、`dp[i]` を部屋 `i` への最短時間とし、`dp[i-1]` と `dp[i-2]` を使って更新した。
116139
- 計算量 `O(N)` なので、大きな `N` に対しても高速に動作する。

0 commit comments

Comments
 (0)