PrevNext

Knuth's Optimization

Introduction

Resources
Jeffrey Xiao
GCP
GFG

Good explanation + Proof of correctness

Knuth's optimization is a special case of Range DP. In general, it is used to solve DP problems with transitions of the form

dp[i][j]=cost[i][j]+min⁡i≤k<j(dp[i][k]+dp[k+1][j]).\texttt{dp}[i][j] = \texttt{cost}[i][j] + \min_{i\leq k<j}(\texttt{dp}[i][k] + \texttt{dp}[k+1][j]).

Further, the cost function must satisfy the following conditions for all a≤b≤c≤da\leq b\leq c \leq d:

  1. cost[b][c]≤cost[a][d]\texttt{cost}[b][c] \leq \texttt{cost}[a][d]
  2. cost[a][c]+cost[b][d]≤cost[a][d]+cost[b][c]\texttt{cost}[a][c] + \texttt{cost}[b][d] \leq \texttt{cost}[a][d] + \texttt{cost}[b][c] (the quadrangle inequality)

Define opt[i][j]\texttt{opt}[i][j] be the index for which dp[i][k]+dp[k+1][j]\texttt{dp}[i][k] + \texttt{dp}[k+1][j] takes on its minimum value, or equivalently,

opt[i][j]=arg min⁡i≤k<j(dp[i][k]+dp[k+1][j]).\texttt{opt}[i][j] = \argmin_{i\leq k<j}(dp[i][k] + dp[k+1][j]).

If more than one such index exists, then take the minimum (or the maximum, doesn't matter). Then, assuming conditions on the dp transition and the cost function to be satisfied, we can show that opt\texttt{opt} satisfies

opt[i][j−1]≤opt[i][j]≤opt[i+1][j].\texttt{opt}[i][j-1] \leq \texttt{opt}[i][j] \leq \texttt{opt}[i+1][j].

The proof of correctness for this claim is in the resources above.

The structure of the code is identical to that of Range DP, with the exception of maintaining the opt\texttt{opt} array. To calculate dp[i][j]\texttt{dp}[i][j] and opt[i][j]\texttt{opt}[i][j], it is only necessary to check values of kk between opt[i][j−1]\texttt{opt}[i][j-1] and opt[i+1][j]\texttt{opt}[i+1][j]. Because of the monotonicity of opt\texttt{opt}, the time complexity of the final algorithm can be shown to be O(N2)\mathcal{O}(N^2).

Problems

StatusSourceProblem NameDifficultyTags
CFEasy
Show TagsDP
SPOJMedium
Show TagsDP
onlinejudge.orgMedium
Show TagsDP
onlinejudge.orgHard
Show TagsDP

Connected-Component DP

Focus Problem – try your best to solve this problem before continuing!

View Internal Solution

Introduction

Let's define a "connected component" as an array of elements with a specific order. We will surround these with brackets. For example, [1,6][1, 6] and [2,3,5][2, 3, 5] are connected components.

Let's define a "collection" of connected components as several non-adjacent connected components without any order. We will surround these with braces. For example, {[1,6],[2,3,5],[7],[4,8,9]}\{[1,6],[2,3,5],[7],[4,8,9]\} is a collection of 4 connected components. {[7],[1,6],[4,8,9],[2,3,5]}\{[7],[1,6],[4,8,9],[2,3,5]\} is another representation of the same collection.

In connected component DP, we can use these components to create a structure given some requirements. This often appears with permutation-related problems.

Connected component DP involves the state dp[i][j]\texttt{dp}[i][j] as some quantity with the first ii elements arranged in jj connected components. There are three main transitions when adding a new element to the collection.

  1. Add it as a new component. Number of components increases. dp[i][j]←dp[i−1][j−1]\texttt{dp}[i][j] \gets \texttt{dp}[i-1][j-1]
  2. Add it onto the end of an existing component. Number of components remains same. dp[i][j]←dp[i−1][j]\texttt{dp}[i][j] \gets \texttt{dp}[i-1][j]
  3. Add it in between two components to merge them. Number of components decreases. dp[i][j]←dp[i−1][j+1]\texttt{dp}[i][j] \gets \texttt{dp}[i-1][j+1]

The quantity for a complete array is dp[n][1]\texttt{dp}[n][1], or the quantity after arranging of all nn elements into 11 component.

Solution - Counting Permutations II

Time Complexity: O(n2)\mathcal{O}(n^2)

This problem requires us to store additional information. Define dp[i][j][k]\texttt{dp}[i][j][k] as the number of valid configurations of the numbers from 11 to ii into jj connected components, where value ii has kk borders where a border is a side lacking a neighbor. For instance, the collection {[1,6],[2,3,5],[7],[4,8,9]}\{[1,6],[2,3,5],[7],[4,8,9]\} would be counted in the state dp[9][4][1]\texttt{dp}[9][4][1].

  1. If ii has no borders, then ii must point two existing connected components.
  2. If ii has one border, then ii must be appended on to an existing connected component.
  3. If ii has two borders, it must be inserted as its own connected component.

This corresponds to the following transitions:

dp[i][j][0]=j(j+1)dp[i−1][j+1][0]+j2⋅dp[i−1][j+1][1]+j(j−1)⋅dp[i−1][j+1][2]dp[i][j][1]=2j⋅dp[i−1][j][0]+(2j−1)⋅dp[i−1][j][1]+(2j−2)⋅dp[i−1][j][2]dp[i][j][2]=dp[i−1][j−1][0]+dp[i−1][j−1][1]+dp[i−1][j−1][2]\begin{align*} \texttt{dp}[i][j][0]&=j(j+1)\texttt{dp}[i-1][j+1][0]+j^2\cdot\texttt{dp}[i-1][j+1][1]+j(j-1)\cdot\texttt{dp}[i-1][j+1][2]\\ \texttt{dp}[i][j][1]&=2j\cdot\texttt{dp}[i-1][j][0]+(2j-1)\cdot\texttt{dp}[i-1][j][1]+(2j-2)\cdot\texttt{dp}[i-1][j][2]\\ \texttt{dp}[i][j][2]&=\texttt{dp}[i-1][j-1][0]+\texttt{dp}[i-1][j-1][1]+\texttt{dp}[i-1][j-1][2]\\ \end{align*}

The base case is dp[0][0][0]=1\texttt{dp}[0][0][0]=1, and the final answer is ∑kdp[n][1][k]\sum_k \texttt{dp}[n][1][k].

#include <bits/stdc++.h>
const int MOD = 1000000007;
int main() {
int N;
std::cin >> N;
std::vector dp(N + 1, std::vector<std::array<long long, 3>>(N + 2));
for (auto &a : dp)
for (auto &b : a) b.fill(0);

Problems

StatusSourceProblem NameDifficultyTags
CEOIMedium
Show TagsDP
CFMedium
Show TagsDP
JOIMedium
Show TagsDP
CFMedium
Show TagsDP
CFMedium
Show TagsDP

Module Progress:

Join the USACO Forum!

Stuck on a problem, or don't understand a module? Join the USACO Forum and get help from other competitive programmers!

PrevNext