Лямбда-оптимизация

Материал из Algocode wiki
Версия от 14:14, 27 сентября 2019; KiKoS (обсуждение | вклад) (Новая страница: «Рассмотрим следующую задачу: Дан массив $a_1, a_2, \dots, a_n$. Надо разбить его на $k$ отрезков так,...»)
(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Рассмотрим следующую задачу: Дан массив $a_1, a_2, \dots, a_n$. Надо разбить его на $k$ отрезков так, чтобы минимизировать сумму квадратов сумм отрезков, $k \le n$.

Сведем задачу к другой — вместо того, чтобы набирать $k$ отрезков, придумаем разбиение на сколько-нибудь отрезков, но за каждый отрезок будем добавлять к функции ответа штраф, равный $\lambda$. Пусть для $\lambda_1$ количество отрезков в разбиении равно $k_1$. Тогда для $\lambda_2 > \lambda_1$ $k_2 \ge k_1$. Интуиция у этого факта такая — если мы за каждый отрезок платим меньше, то мы можем взять больше отрезков. Понятно, что если данное неравенство не выполняется, то лямбда-оптимизация не работает.

Поскольку зависимость между $k$ и $\lambda$ монотонная, то можно сделать бинарный поиск по $\lambda$ и найти такую, при которой $k_\lambda=k$. Новую задачу можно решать при помощи convex hull trick, потому что $$dp_i = \max_{j=1}^{i-1} dp[j] + \lambda + pref_i^2 - (2 \cdot pref_j) \cdot pref_i + pref_j^2$$, где $pref$ — массив префиксных сумм.

 1 pair<int, int> calc(int X) {
 2 	vector<pair<int, int>> dp(n + 1);
 3 	for (int i = 1; i <= n; i++) {
 4 		dp[i] = f(i);
 5 	}
 6 	return dp[n];
 7 }
 8 /*
 9 .
10 .
11 .
12 */
13 
14 int L = -INF;
15 int R = INF;
16 while (L + 1 < R) {
17     int mid = (L + R) / 2;
18 	pair<int, int> X = calc(mid);
19 	if (X.second > k) {
20 		R = mid;
21 	}
22 	else {
23 		L = mid;
24 	}
25 }
26 pair<int, int> res = check(R);
27 cout << res.first - k * R;
28 ...



Автор конспекта: Константин Амеличев

По всем вопросам пишите в telegram @kik0s