Luzhiled's Library

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub ei1333/library

:heavy_check_mark: Online Offline DP(オンライン・オフライン変換) (dp/online-offline-dp.hpp)

概要

$dp[j] = \min \{ dp[i] + f(i,j) : i \in [0,j) \}$ (例: $f(i,j)$ を区間 $[i,j)$ のコストとすると、区間[0, j) を任意個に分割するときの最小コスト) を考える.

愚直にDPをすると $O(N^2)$ かかるが, 最小値をとる位置 $i$ が $j$ の増加にしたがって単調増加する場合 $O(N \log^2 N)$ で解くことができる.

区間 $[l, r)$ の dp 配列を埋めたい. $m = \frac {l + r} 2$ とする. 遷移するときに $m$ をまたがないものは分割して再帰的に解くことにすると, $m$ をまたぐものだけを考えれば良い. $[l, m)$ の dp 配列の値はわかり, それ以前の dp の計算結果に依存しない形となる. つまり $2$ 変数関数 $g(j, i) = dp[i] + f(i, j) (l \le i \lt m, m \leq j \lt r)$ を定義できて, Monotone-Minima を用いて効率的に解くことができる. (日本語むずかしい こまった)(分割統治FFTも同じ考え方なので下のコードのinduceの部分をFFTにするとできると思う)

計算量

Depends on

Verified with

Code

#include "monotone-minima.hpp"

template <typename T, typename Compare = less<T> >
vector<T> online_offline_dp(int W, const function<T(int, int)> &f,
                            const Compare &comp = Compare()) {
  vector<T> dp(W + 1);
  vector<int> isset(W + 1);
  int y_base = -1, x_base = -1;
  function<T(int, int)> get_cost =
      [&](int y, int x) {  // return dp[0, x+x_base)+f[x+x_base, y+y_base)
        return dp[x + x_base] + f(x + x_base, y + y_base);
      };
  function<void(int, int, int)> induce = [&](int l, int m,
                                             int r) {  // dp[l, m) -> dp[m, r)
    x_base = l, y_base = m;
    auto ret = monotone_minima(r - m, m - l, get_cost, comp);
    for (int i = 0; i < ret.size(); i++) {
      if (!isset[m + i] || comp(ret[i].second, dp[m + i])) {
        isset[m + i] = true;
        dp[m + i] = ret[i].second;
      }
    }
  };
  function<void(int, int)> dfs = [&](int l, int r) {
    if (l + 1 == r) {
      x_base = l, y_base = l;
      T cst = l ? get_cost(0, -1) : 0;
      if (!isset[l] || comp(cst, dp[l])) {
        isset[l] = true;
        dp[l] = cst;
      }
    } else {
      int mid = (l + r) / 2;
      dfs(l, mid);
      induce(l, mid, r);
      dfs(mid, r);
    }
  };
  dfs(0, W + 1);
  return dp;
};
#line 1 "dp/monotone-minima.hpp"
template <typename T, typename Compare = less<T> >
vector<pair<int, T> > monotone_minima(int H, int W,
                                      const function<T(int, int)> &f,
                                      const Compare &comp = Compare()) {
  vector<pair<int, T> > dp(H);
  function<void(int, int, int, int)> dfs = [&](int top, int bottom, int left,
                                               int right) {
    if (top > bottom) return;
    int line = (top + bottom) / 2;
    T ma;
    int mi = -1;
    for (int i = left; i <= right; i++) {
      T cst = f(line, i);
      if (mi == -1 || comp(cst, ma)) {
        ma = cst;
        mi = i;
      }
    }
    dp[line] = make_pair(mi, ma);
    dfs(top, line - 1, left, mi);
    dfs(line + 1, bottom, mi, right);
  };
  dfs(0, H - 1, 0, W - 1);
  return dp;
}
#line 2 "dp/online-offline-dp.hpp"

template <typename T, typename Compare = less<T> >
vector<T> online_offline_dp(int W, const function<T(int, int)> &f,
                            const Compare &comp = Compare()) {
  vector<T> dp(W + 1);
  vector<int> isset(W + 1);
  int y_base = -1, x_base = -1;
  function<T(int, int)> get_cost =
      [&](int y, int x) {  // return dp[0, x+x_base)+f[x+x_base, y+y_base)
        return dp[x + x_base] + f(x + x_base, y + y_base);
      };
  function<void(int, int, int)> induce = [&](int l, int m,
                                             int r) {  // dp[l, m) -> dp[m, r)
    x_base = l, y_base = m;
    auto ret = monotone_minima(r - m, m - l, get_cost, comp);
    for (int i = 0; i < ret.size(); i++) {
      if (!isset[m + i] || comp(ret[i].second, dp[m + i])) {
        isset[m + i] = true;
        dp[m + i] = ret[i].second;
      }
    }
  };
  function<void(int, int)> dfs = [&](int l, int r) {
    if (l + 1 == r) {
      x_base = l, y_base = l;
      T cst = l ? get_cost(0, -1) : 0;
      if (!isset[l] || comp(cst, dp[l])) {
        isset[l] = true;
        dp[l] = cst;
      }
    } else {
      int mid = (l + r) / 2;
      dfs(l, mid);
      induce(l, mid, r);
      dfs(mid, r);
    }
  };
  dfs(0, W + 1);
  return dp;
};
Back to top page