Skip to content

Commit

Permalink
Merge pull request #4435 from RadialFunc/patch-1
Browse files Browse the repository at this point in the history
Update DNQ fixes issue #4418
  • Loading branch information
SansPapyrus683 authored Apr 12, 2024
2 parents 1c9aa1a + 82767f5 commit c192b4b
Showing 1 changed file with 131 additions and 12 deletions.
143 changes: 131 additions & 12 deletions content/5_Plat/DC-DP.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ prerequisites:
- convex-hull-trick
---

Allows you to reduce $\mathcal{O}(N^2)$ to $\mathcal{O}(N\log N)$.

## Tutorial
## Overview

<Resources>
<Resource
Expand All @@ -26,20 +24,139 @@ Allows you to reduce $\mathcal{O}(N^2)$ to $\mathcal{O}(N\log N)$.
<Resource source="GCP" title="15.4.2 - Divide & Conquer Optimization" />
</Resources>

## Example - Circular Barn
Consider a dynamic programming problem with the following formula

<FocusProblem problem="dnc_sample" />
$$
dp(i,j) = \min_{0\leq k \leq j} ( dp(i-1, k-1) + C(k,j)),
$$

You should already be familiar with the [CHT solution](/plat/convex-hull-trick#problems).
where $C(i,j)$ is a cost function and you can compute it in $O(1)$ time.
Furthermore, $dp(i,j) =0$ for $j<0$.

The straightforward implementation gives a runtime of $O(MN^2)$ if $0\leq i < M$ and $0\leq j < N$.
Divide & Conquer DP allows this to be optimized to $O(M N \log N)$.

For each $i,j$, let $\text{opt}(i,j)$ be the value of $k$ that minimizes the right hand side of the equation.
Divide & Conquer DP **only applies if**

$$
\text{opt}(i,j) \leq \text{opt}(i,j+1).
$$

<IncompleteSection>
Often, proving this with the given cost function is challenging,
but if the cost function satisfies the [quadrangle inequality](https://codeforces.com/blog/entry/86306), the condition holds.

Add analysis later.
We can then apply the idea behind Divide & Conquer.
Fix a given $i$. First, compute $\text{opt}(i,n/2)$.
Then compute $\text{opt}(i, n/4)$ using the fact that it is less than or equal to $\text{opt}(i, n/2)$.
Similarly, we can compute $\text{opt}(i, 3n/4)$ and recursively split the ranges in half, keeping track on the lower and upper bounds.

</IncompleteSection>
Since each possible value of $\text{opt}(i, j)$ appears $O(\log n)$ times, this gives a final runtime of $O(mn \log n)$.

## Example - Circular Barn

<FocusProblem problem="dnc_sample" />

You should already be familiar with the [CHT solution](/plat/convex-hull-trick#problems).

Check the
[official editorial](http://www.usaco.org/current/data/sol_cbarn_platinum_feb16.html).
### Explanation

We iterate through the possibilities of the location of the first door.
For each of the first doors, we can now view the barn linearly.
All further calculations will be done assuming the barn is a linear sequence of doors starting at the first opened door.

Let $dp(i,k)$ denote the location of the last door if we place $k$ doors optimally among the first $i$ rooms.
The idea is that $dp(i,k) \leq dp(i+1, k)$.
Assume this was not true for the sake of contradiction.
Then $dp(i,k) > dp(i+1,k)$, so we also have $dp(i+1,k) \leq i$.
But then we could have used the best possible setup for $(i+1,k)$ in the $(i,k)$ setup as well, since all open doors are among the first $i$ rooms anyways.

Since the monotonicity condition is held, we can now perform Divide & Conquer DP.
Fix the value of $k$ and compute $dp(n/2, k)$.
Then compute it for the left and right halves of the array.

### Implementation

**Time Complexity:** $\mathcal{O}(n^2 k \log n)$, since we need to check $n$ rooms for the optimal first barn position.

<LanguageSection>
<CPPSection>

```cpp
#include <bits/stdc++.h>
using namespace std;
#define ll long long

const int MAX_N = 1000;
const int MAX_K = 7;

// calc[i][j] stores the # of steps to get all cows distance j away to door i
// to make implementing a cyclic array easier, we double the size
vector<vector<ll>> calc(2 * MAX_N, vector<ll>(MAX_N + 1, 0));
// dp[i][j] stores the answer for doors 0,1,.., j and i doors open
vector<vector<ll>> dp(MAX_K + 1, vector<ll>(MAX_N + 1, INT64_MAX));

int rot;

void compdp(int k, int begin, int end, int rl, int rr) {
// fixed k, begin and end are the ends of the array, rl and rr are the
// bounds on the last door used
int mid = (begin + end) / 2;

ll best = INT64_MAX;
int best_last = -1;
// best is min amount moved, last is the last door used
for (int last = rl; last <= min(mid, rr); last++) {
ll cost = dp[k - 1][last - 1] + calc[last + rot][mid - last + 1];
if (cost < best) {
best = cost;
best_last = last;
} else if (cost == best && last < best_last) {
best_last = last;
}
}
dp[k][mid] = best;
if (begin == end) { return; }
compdp(k, begin, mid, rl, best_last);
compdp(k, mid + 1, end, best_last, rr);
}

int main() {
freopen("cbarn.in", "r", stdin);
int n, k;
cin >> n >> k;

vector<int> a(2 * n);
for (int i = 0; i < n; i++) {
cin >> a[i];
a[i + n] = a[i];
}

for (int i = 0; i < n; i++) {
for (int j = 1; j <= n; j++) {
calc[i][j] = calc[i + n][j] =
calc[i][j - 1] + (ll)a[i + j - 1] * (j - 1);
}
}

// rotate stores where we start in the linear representation
ll ans = INT64_MAX;
for (rot = 0; rot < n; rot++) {
for (int i = 0; i < n; i++) { dp[0][i] = calc[rot][i + 1]; }
for (int i = 1; i < k; i++) {
for (int j = 0; j < n; j++) { dp[i][j] = INT64_MAX; }
compdp(i, i, n - 1, i, n - 1);
}
ans = min(ans, dp[k - 1][n - 1]);
}

freopen("cbarn.out", "w", stdout);
cout << ans << endl;
}
```

</CPPSection>
</LanguageSection>

## Problems

Expand All @@ -54,6 +171,8 @@ that you swap do not count towards the minimum number of bubble sort swaps.

<!-- <Info title="Pro Tip">

There are [plenty](https://github.com/bqi343/USACO/blob/master/Contests/USACO%20Links/Division-Specific/Platinum.md) of Platinum DP problems that are not covered by this guide. We recommend that you work through these on your own.
There are [plenty](https://github.com/bqi343/USACO/blob/master/Contests/USACO%20Links/Division-Specific/Platinum.md)
Platinum DP problems that are not covered by this guide.
We recommend that you work through these on your own.

</Info> -->

0 comments on commit c192b4b

Please sign in to comment.