Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

動的計画法

概要

動的計画法(Dynamic Programming, DP) は、問題を小さな部分問題に分割し、その結果を記録・再利用することで効率的に解くアルゴリズム設計手法である。

適用条件

性質説明
最適部分構造問題の最適解が部分問題の最適解から構成できる
重複する部分問題同じ部分問題が複数回現れる(単純な再帰では無駄な計算が発生する)

貪欲法との違い

貪欲法動的計画法
選択方法局所最適を逐次選択全部分問題の最適解を記録して利用
後戻りしないしない(ただし全パターンを網羅)
適用範囲貪欲選択性が成立する問題最適部分構造 + 重複部分問題がある問題
計算量一般に速い貪欲法より遅いが再帰より大幅に速い

2つのアプローチ

  • トップダウン(メモ化再帰): 再帰で解きながら、計算済みの結果をキャッシュする

  • ボトムアップ(表形式): 小さい部分問題から順に解いてテーブルに埋める

例1: フィボナッチ数列

F(n)=F(n−1)+F(n−2)F(n) = F(n-1) + F(n-2) は重複する部分問題の典型例。 単純な再帰では O(2n)O(2^n) かかるが、DP では O(n)O(n) になる。

102 μs ± 730 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
200 ns ± 2.37 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

例2: 0/1 ナップサック問題

Q. 容量 WW のナップサックに重さ wiw_i・価値 viv_i のアイテムを詰めるとき、価値を最大化するには? アイテムは分割できない(0/1: 使うか使わないか)。

分数ナップサックと異なり、貪欲法では最適解が保証されないため DP を使う。

漸化式

dp[i][j]=max⁡(dp[i−1][j],  dp[i−1][j−wi]+vi)(j≥wi)dp[i][j] = \max(dp[i-1][j],\; dp[i-1][j - w_i] + v_i) \quad (j \geq w_i)
  • dp[i][j]dp[i][j]: 最初の ii 個のアイテムから容量 jj で得られる最大価値

最大価値: 220
  ※ 分数ナップサックの場合は 240 になる

例3: 最長共通部分列(LCS)

Q. 2つの文字列 ss、tt の最長共通部分列(Longest Common Subsequence)の長さは?

部分列は連続でなくてもよい(例: "ACE" は "ABCDE" の部分列)。

漸化式

dp[i][j]={dp[i−1][j−1]+1(s[i]=t[j])max⁡(dp[i−1][j],  dp[i][j−1])(s[i]≠t[j])dp[i][j] = \begin{cases} dp[i-1][j-1] + 1 & (s[i] = t[j]) \\ \max(dp[i-1][j],\; dp[i][j-1]) & (s[i] \neq t[j]) \end{cases}
s = 'ABCBDAB'
t = 'BDCAB'
LCS の長さ: 4
LCS の例 : 'BDAB'