Optimise a recurrence of the form

from to , when the optimal split point is monotone:

The algorithm

For a fixed layer , solve the range knowing lies in :

  1. Take the midpoint .
  2. Compute by scanning , recording the argmin .
  3. Recurse on with the range and on with .
vector<long long> prev_, cur;
 
void compute(int l, int r, int optL, int optR) {
    if (l > r) return;
    int m = (l + r) / 2;
    pair<long long,int> best = {LLONG_MAX, -1};
    for (int k = optL; k <= min(m, optR); k++)
        best = min(best, {prev_[k] + C(k, m), k});
    cur[m] = best.first;
    int opt = best.second;
    compute(l, m - 1, optL, opt);
    compute(m + 1, r, opt, optR);
}
 
long long solve(int n, int layers) {
    prev_ = baseLayer(n);
    for (int i = 1; i <= layers; i++) {
        cur.assign(n + 1, LLONG_MAX);
        compute(0, n, 0, n);
        swap(prev_, cur);
    }
    return prev_[n];
}

Complexity: recursion levels, and at each level the scanned -ranges overlap only at their endpoints, totalling work. So per layer, overall.

When does monotonicity hold?

Sufficient condition: satisfies the quadrangle inequality (concave Monge)

Cost functions that satisfy it in practice:

  • (sum of a range), or any convex function of a range sum
  • number of inversions / pairs inside
  • cost of grouping into one cluster with a central representative
  • number of distinct values in

Verify before trusting

Brute-force the DP on small random inputs, record the argmins, and check they are non-decreasing. Thirty seconds of checking beats debugging a wrong answer. See Yao’s theorem for the formal conditions.

Computing efficiently

The recursion assumes is . When it is not, exploit the fact that consecutive evaluations move and by one — maintain the value with a two-pointer / Mo’s-style incremental structure:

int curL, curR; long long curCost;
long long C(int l, int r) {
    while (curR < r) add(++curR);
    while (curL > l) add(--curL);
    while (curR > r) remove(curR--);
    while (curL < l) remove(curL++);
    return curCost;
}

The amortized cost is total, matching the recursion — this combination is the standard solution to “partition an array into segments minimising the total number of equal pairs” style problems.

The optimisation family

RecurrenceRequirementTechniqueTime
monotone optD&C DP
same, offline, totally monotoneMongeSMAWK
same, onlineMongeLARSCH
QI + monotoneKnuth
linesCHT /
fixed-width windownonemonotone queue
”exactly groups” with convex cost in convexityAliens trick

D&C DP vs Aliens trick

Both attack “partition into exactly parts”. D&C DP keeps the dimension and costs ; Aliens trick removes it, costing times the cost of one unconstrained solve. When is large (say ), Aliens is the only option. When is small, D&C DP is simpler and has no convexity requirement on the answer-vs- curve.

See also: Knuth Optimization · Aliens Trick · SMAWK