KisaragiEffective.github.io

Yeet.

View on GitHub

Essence of 動的計画法

( ´・ω・`) つ [jawp:動的計画法]

概要: 再帰は読みやすいけど、スタック・オーバーフローしやすいし、時間オーダーも読みにくい。それってせっかく計算式がわかっててもTLEするかもしれないってことでしょ? 末尾再帰化 (a.k.a tailrec)もいいけど言語によりけりだし。なので、値を列挙してみよう!w

目標

適用要件

レシピ

例1: フィボナッチ数列

再帰として実装すると以下のようになる (ここで、このコードのオーダーは$O(\phi^N)$ - つまり指数時間)

fun fib(n: Int): Int {
  if (n == 0) return 0
  if (n == 1) return 1
  return fib(n-1) + fib(n-2)
}

シンプルだが、これは時間がかかりすぎる。$N$が指数に来ているので、あっという間に爆発してしまう。TLEでお亡くなりになってしまうので、タイトルに含まれるように動的計画法を用いて書き直してみたい。 しかしその前に、適用要件とレシピを確認してみたいと思う。

適用要件

レシピ

では実装してみよう。

val MAX = 30000
// ここで、まだ格納されていないことを示すのに-1を用いた
val dp = IntArray(MAX) { _ -> -1 }

// O(n)
fun init() {
  dp[0] = 0
  dp[1] = 1
  for (i in 2..MAX) {
    dp[i] = dp[i - 1] + dp[i - 2]
  }
}

// O(1)
fun fib(n: Int): Int {
  return dp[n]
}

指数時間より遥かに良くなった。線形時間なんて可愛いもんだ。