Skip to content

Commit 560ed3e

Browse files
Merge pull request #4382 from A1exL1ang/master
aliens trick 👽
2 parents 4b2ef69 + 14e06c8 commit 560ed3e

File tree

2 files changed

+178
-18
lines changed

2 files changed

+178
-18
lines changed

content/6_Advanced/Lagrange.mdx

+140-6
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
---
22
id: lagrange
33
title: 'Lagrangian Relaxation'
4-
author: Benjamin Qi
4+
author: Benjamin Qi, Alex Liang, Dong Liu
55
description: 'aka Aliens Trick'
66
prerequisites:
77
- convex-hull
88
frequency: 1
99
---
1010

11-
adding lambda\*smth
12-
13-
<Problems problems="sample" />
14-
15-
## Tutorial
11+
## Resources
1612

1713
<Resources>
1814
<Resource
@@ -27,6 +23,144 @@ adding lambda\*smth
2723
/>
2824
</Resources>
2925

26+
## Lagrangian Relaxation
27+
28+
Lagrangian Relaxation involves transforming a constraint on a variable into a cost $\lambda$ and binary searching for the optimal $\lambda$.
29+
30+
<FocusProblem problem="sample" />
31+
32+
The problem gives us a length $N$ ($1 \le N \le 3 \cdot 10^5$) array of integers in the range $[-10^9,10^9]$. We are given some $K$ ($1 \le K \le N$) and are asked to choose at most $K$ disjoint subarrays such that the sum of elements included in a subarray is maximized.
33+
34+
### Intuition
35+
36+
The main bottleneck of any dynamic programming solution to this problem is having to store the number of subarrays we have created so far.
37+
38+
Let's try to find a way around this. Instead of storing the number of subarrays we have created so far, we assign a penalty of $\lambda$ for creating a new subarray (i.e. every time we create a subarray we penalize our sum by $\lambda$).
39+
40+
This leads us to the sub-problem of finding the maximal sum and number of subarrays used if creating a new subarray costs $\lambda$. We can solve this in $\mathcal{O}(N)$ time with dynamic programming.
41+
42+
<Spoiler title="Dynamic Programming Solution">
43+
Let's have $\texttt{dp}[i][j:\{0,1\}]$ represent the maximum sum if we consider the first $i$ elements, given that $j=0/1$ implies whether
44+
element $i$ is part of a subarray. Let $\texttt{cnt}[i][j]$ represent the number of
45+
people used in an optimal arrangement of $\texttt{dp}[i][j]$.
46+
47+
For our $\texttt{dp}$ transitions, we have
48+
49+
$$
50+
\{\texttt{dp}[i][0], \texttt{cnt}[i][0]\} =
51+
\max\begin{cases} \{\texttt{dp}[i - 1][0], \texttt{cnt}[i - 1][0]\}\\
52+
\{\texttt{dp}[i - 1][1], \texttt{cnt}[i - 1][1]\}
53+
\end{cases}
54+
$$
55+
56+
and
57+
58+
$$
59+
\{\texttt{dp}[i][1], \texttt{cnt}[i][1]\} =
60+
\max\begin{cases}\{\texttt{dp}[i - 1][0] + A[i] - \lambda, \texttt{cnt}[i - 1][0] + 1\}\\
61+
\{\texttt{dp}[i - 1][1] + A[i], \texttt{cnt}[i - 1][1]\}\end{cases}
62+
$$
63+
64+
because we either begin a new subarray or we continue an existing subarray.
65+
</Spoiler>
66+
67+
Let $v$ be the maximal achievable sum with $\lambda$ penalty and $c$ be the number of subarrays used to achieve $v$. Then the **maximal possible sum achievable if we use exactly $c$ subarrays is $v+\lambda c$**. Note that we add $\lambda c$ to undo the penalty.
68+
69+
Our goal is to find some $\lambda$ such that $c=K$ (assuming $K$ is at most the number of positive elements). As we increase $\lambda$, it makes sense for $c$ to decrease since we are penalizing subarrays more. Thus, we can try to binary search for $\lambda$ to make $c=K$ and set our answer to be $v+\lambda c$ at the optimal $\lambda$.
70+
71+
This idea almost works but there are still some very important caveats and conditions that we have not considered.
72+
73+
### Geometry
74+
75+
Let $f(x)$ be the maximal sum if we use at most $x$ subarrays. We want to find $f(K)$.
76+
77+
The first condition is that $f(x)$ **must be concave or convex**. Since $f(x)$ is increasing in this problem, the means that we need $f(x)$ to be concave: $f(x) - f(x - 1) \ge f(x + 1) - f(x)$. In other words, this means that the more subarrays we add, the less we increase the sum by. We can intuitively see that this is true.
78+
79+
<Spoiler title="Proof that our function is concave">
80+
81+
We construct a flow graph with source $S$, sink $T$, and $N+1$ additional vertices numbered $1$ to $N+1$. We will have the following edges.
82+
83+
- A directed edge from $S$ to $i$ ($1 \le i \le N+1$) with weight $0$ and capacity $1$.
84+
85+
- A directed edge from $i$ ($1 \le i \le N+1$) to $T$ with weight $0$ and capacity $1$.
86+
87+
- A bidirectional edge from $i$ ($1 \le i \le N$) to $i+1$ with weight $A[i]$ and capacity $1$.
88+
89+
$f(x)$ will be the maximum cost $x$-flow through the graph. We can repeatedly find the maximum cost augmenting path $x$ times to get our answer. Because the maximum cost as a function of flow is concave, $f(x)$ will be concave. You can read more about simulating cost flows [here](https://codeforces.com/blog/entry/118391).
90+
91+
</Spoiler>
92+
93+
Consider the following graphs of $f(x)$ and $f(x)-\lambda x$. In this example, we have $\lambda=5$.
94+
95+
<iframe
96+
src="https://www.desmos.com/calculator/ydynwc2fej?embed"
97+
width="500px"
98+
height="300px"
99+
frameborder="0"
100+
/>
101+
102+
Here is where the fact that $f(x)$ is concave comes in. Because the slope is non-increasing, we know that $f(x) - \lambda x$ will **first increase, then stay the same, and finally decrease**.
103+
104+
Let $v(\lambda)$ be the optimal maximal achievable sum with $\lambda$ penalty and $c(\lambda)$ be the number of subarrays used to achieve $v(\lambda)$ (note that if there are multiple such possibilities, we set $c(\lambda)$ to be the **maximal** number of subarrays to achieve $v(\lambda)$). These values can be calculated in $\mathcal{O}(N)$ time using the dynamic programming approach described above.
105+
106+
When we assign the penalty of $\lambda$, we are trying to find the maximal sum if creating a subarray reduces our sum by $\lambda$. So $v(\lambda)$ will be the **maximum** of $f(x) - \lambda x$ and $c(\lambda)$ will equal to the rightmost $x$ that **maximizes** $f(x) - \lambda x$.
107+
108+
Given the shape of $f(x) - \lambda x$, we know that $f(x) - \lambda x$ will be maximized at all points where $\lambda$ is equal to the slope of $f(x)$ (these points are red in the graph above). If there are no such points it will be maximized at the rightmost point where the slope is less than $\lambda$. So this means that $c(\lambda)$ will be the rightmost $x$ at which the slope of $f(x)$ is still greater or equal to $\lambda$.
109+
110+
111+
Now we know exactly what $\lambda$ represents: $\lambda$ is the slope and $c(\lambda)$ is the rightmost $x$ at which the slope of $f(x)$ is still greater or equal to $\lambda$.
112+
113+
We binary search for $\lambda$ and find the highest $\lambda$ such that $c(\lambda) \ge K$. Let the optimal value be $\lambda_{\texttt{opt}}$. Then our answer is $v(\lambda_{\texttt{opt}}) + \lambda_{\texttt{opt}} K$. Note that this works even if $c(\lambda_{\texttt{opt}}) \neq K$ since $c(\lambda_{\texttt{opt}})$ and $K$ will be on the same line with slope $\lambda_{\texttt{opt}}$ in that case.
114+
115+
Because calculating $v(\lambda)$ and $c(\lambda)$ with the dynamic programming solution described above will take $\mathcal{O}(N)$ time, this solution runs in $\mathcal{O}(N\log{\sum A[i]})$ time.
116+
117+
```cpp
118+
#include <bits/stdc++.h>
119+
using namespace std;
120+
121+
#define ll long long
122+
123+
int main() {
124+
int n, k;
125+
cin >> n >> k;
126+
127+
int a[n];
128+
for (int &i : a) { cin >> i; }
129+
130+
/**
131+
* @return the maximum sum along with the number of subarrays used
132+
* if creating a subarray penalizes the sum by "lmb" and
133+
* there is no limit to the number of subarrays you can create
134+
*/
135+
auto solve_lambda = [&](ll lmb) {
136+
pair<ll, ll> dp[n][2];
137+
138+
dp[0][0] = {0, 0};
139+
dp[0][1] = {a[0] - lmb, 1};
140+
141+
for (int i = 1; i < n; i++) {
142+
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]);
143+
144+
dp[i][1] =
145+
max(make_pair(dp[i - 1][0].first + a[i] - lmb,
146+
dp[i - 1][0].second + 1),
147+
make_pair(dp[i - 1][1].first + a[i], dp[i - 1][1].second));
148+
}
149+
150+
return max(dp[n - 1][0], dp[n - 1][1]);
151+
};
152+
153+
ll lo = 0;
154+
ll hi = 1e18;
155+
while (lo < hi) {
156+
ll mid = (lo + hi + 1) / 2;
157+
solve_lambda(mid).second >= k ? lo = mid : hi = mid - 1;
158+
}
159+
160+
cout << solve_lambda(lo).first + lo * k << endl;
161+
}
162+
```
163+
30164
## Problems
31165
32166
<Problems problems="probs" />

content/6_Advanced/Lagrange.problems.json

+38-12
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,30 @@
22
"MODULE_ID": "lagrange",
33
"sample": [
44
{
5-
"uniqueId": "usaco-697",
6-
"name": "Tall Barn",
7-
"url": "http://www.usaco.org/index.php?page=viewproblem2&cpid=697",
8-
"source": "Platinum",
5+
"uniqueId": "noisg-19-feast",
6+
"name": "2019 - Feast",
7+
"url": "https://oj.uz/problem/view/NOI19_feast",
8+
"source": "NOI.sg",
99
"difficulty": "Easy",
1010
"isStarred": false,
1111
"tags": [],
1212
"solutionMetadata": {
13-
"kind": "USACO",
14-
"usacoId": "697"
13+
"kind": "internal"
1514
}
1615
}
1716
],
1817
"probs": [
1918
{
20-
"uniqueId": "noisg-19-feast",
21-
"name": "2019 - Feast",
22-
"url": "https://oj.uz/problem/view/NOI19_feast",
23-
"source": "NOI.sg",
24-
"difficulty": "Very Easy",
19+
"uniqueId": "usaco-697",
20+
"name": "Tall Barn",
21+
"url": "http://www.usaco.org/index.php?page=viewproblem2&cpid=697",
22+
"source": "Platinum",
23+
"difficulty": "Easy",
2524
"isStarred": false,
2625
"tags": [],
2726
"solutionMetadata": {
28-
"kind": "internal"
27+
"kind": "USACO",
28+
"usacoId": "697"
2929
}
3030
},
3131
{
@@ -41,6 +41,32 @@
4141
"site": "CF"
4242
}
4343
},
44+
{
45+
"uniqueId": "cf-1661F",
46+
"name": "Teleporters",
47+
"url": "https://codeforces.com/problemset/problem/1661/F",
48+
"source": "CF",
49+
"difficulty": "Normal",
50+
"isStarred": true,
51+
"tags": [],
52+
"solutionMetadata": {
53+
"kind": "autogen-label-from-site",
54+
"site": "CF"
55+
}
56+
},
57+
{
58+
"uniqueId": "fhc-Vacation",
59+
"name": "Vacation",
60+
"url": "https://www.facebook.com/codingcompetitions/hacker-cup/2021/final-round/problems/D",
61+
"source": "FHC",
62+
"difficulty": "Normal",
63+
"isStarred": false,
64+
"tags": [],
65+
"solutionMetadata": {
66+
"kind": "autogen-label-from-site",
67+
"site": "FHC"
68+
}
69+
},
4470
{
4571
"uniqueId": "kattis-blazingnewtrails",
4672
"name": "Blazing New Trails",

0 commit comments

Comments
 (0)