<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=188.170.214.39&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=188.170.214.39&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/188.170.214.39"/>
		<updated>2026-06-11T14:24:22Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=64542</id>
		<title>Convex hull trick</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=64542"/>
				<updated>2018-03-21T19:54:34Z</updated>
		
		<summary type="html">&lt;p&gt;188.170.214.39: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull trick {{---}} один из методов оптимизации [[Динамическое_программирование | динамического программирования]], использующий идею [[Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull|выпуклой оболочки]]. Позволяет улучшить асимптотику решения некоторых задач, решемых методом динамического программирования, с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO {{---}} национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
&lt;br /&gt;
==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
Рассмотрим задачу на ДП:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
бензопилы. Но пила устроена так, что она может спиливать только по &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; метру от дерева, к которому ее применили. Также после&lt;br /&gt;
срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость &lt;br /&gt;
бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки &lt;br /&gt;
равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
(убывание и возрастание нестрогие)&lt;br /&gt;
}}&lt;br /&gt;
(Задача H с Санкт-Петербургских сборов к РОИ 2016 &amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Наивное решение==&lt;br /&gt;
Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; {{---}} минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;, по условию задачи.&lt;br /&gt;
Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме [[Теорема_Радо-Эдмондса_(жадный_алгоритм)|жадных алгоритмов]], чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева номер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили, имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, что ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
Итоговая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим на код вышеописанного решения:&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
    dp[1] = 0&lt;br /&gt;
    dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for''' i = 2 = 1..n  &lt;br /&gt;
       dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''for''' j = 1..i - 1 &lt;br /&gt;
           '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
               dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
 '''return''' dp[n]&lt;br /&gt;
Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Ключевая идея оптимизации==&lt;br /&gt;
Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; {{---}} это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture1convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Выделим множество точек &amp;lt;tex&amp;gt;(x_0, y_0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x_0) &amp;lt; y_0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
&lt;br /&gt;
==Цель нижней огибающей множества прямых==&lt;br /&gt;
Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координатам x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти [[Целочисленный_двоичный_поиск|бинарным поиском]]. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(x_L, y_L)&amp;lt;/tex&amp;gt; {{---}} точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(x_R, y_R)&amp;lt;/tex&amp;gt; {{---}} точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;x_L&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;x_R&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(x_L \geqslant x_R)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе - остановимся. Так будем делать, пока либо число прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;x_L&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;x_R.&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture2convexhull.png]]&lt;br /&gt;
[[Файл:picture3convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th1239. &lt;br /&gt;
|statement=Алгоритм построения нижней огибающей множества прямых корректен.&lt;br /&gt;
|proof=Достаточно показать, что последнюю прямую нужно удалить из множества &amp;lt;tex&amp;gt;\Leftrightarrow&amp;lt;/tex&amp;gt;, когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя - предпоследнюю.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; {{---}} уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; {{---}} уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; x_R]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [x_L; + \infty]  : y[sz - 1](x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;x_L &amp;lt; x_R&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [x_L; x_R]  : y[sz - 1] \geqslant y[sz](x) и Y(x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[x_L; x_R]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;x_L &amp;gt; x_R&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[x_L; x_R] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Детали реализации:==&lt;br /&gt;
Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; {{---}} номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] \geqslant front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив &amp;lt;math&amp;gt;front[]&amp;lt;/math&amp;gt; можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (&amp;lt;math&amp;gt;z&amp;lt;/math&amp;gt;-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
     st[1] = 1&lt;br /&gt;
     from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
     sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
     pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] \geqslant front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for'''  i = 2..n  &lt;br /&gt;
          '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
               pos = pos + 1&lt;br /&gt;
          j = st[pos]&lt;br /&gt;
          dp[i] = K[j] &amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt; a[i] + B[j] &lt;br /&gt;
          '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
                K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
                '''while''' ''true'' &lt;br /&gt;
                      j = st[sz]&lt;br /&gt;
                      x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                      '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                      sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
                 st[sz + 1] = i  &lt;br /&gt;
                 from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
                 sz = sz + 1&lt;br /&gt;
  '''return''' dp[n] &lt;br /&gt;
Здесь функция &amp;lt;tex&amp;gt;\mathtt{divide(a, b)}&amp;lt;/tex&amp;gt; возвращает нужное(*) округление &amp;lt;tex&amp;gt;\frac{a}{b}&amp;lt;/tex&amp;gt;. Приведем её код :&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
        delta = 0&lt;br /&gt;
        '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
        '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
 '''return''' -[|a| / |b|]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Такая реализация будет работать за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Динамический convex hull trick==&lt;br /&gt;
	Заметим, что условия на возрастание/убывание &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; на убывание/возрастание и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой - отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016 &amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
	Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, с помощью которой мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая - та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x_1; x_2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x_1, x_2 \in R&amp;lt;/tex&amp;gt; - точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x_2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:picture4convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Чтобы уметь вставлять прямую в множество будем хранить [[Красно-черное_дерево|двоичное дерево поиска]], в вершинах которого будут пары &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  (коэффицент прямой, ее номер в глобальной нумерации). Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
	&lt;br /&gt;
Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Альтернативный подход ==&lt;br /&gt;
Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос {{---}} это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture5convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Докажем то, что описанный выше алгоритм корректен. Для этого достаточно показать, что если имеются &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; вектора &amp;lt;math&amp;gt;a, b, c&amp;lt;/math&amp;gt;, расположенные как на рис. 5, т.е. точка &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; не лежит на выпуклой оболочке векторов &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt; \Leftrightarrow [a-b, b-c] &amp;lt; 0 &amp;lt;/tex&amp;gt;, то либо  &amp;lt;tex&amp;gt;(a, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;,  либо &amp;lt;tex&amp;gt;(c, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th12392. &lt;br /&gt;
|statement=Если есть &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; вектора &amp;lt;tex&amp;gt;a, b, c&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;[a-b, b-c] &amp;lt; 0&amp;lt;/tex&amp;gt; то либо &amp;lt;math&amp;gt;(a, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, либо &amp;lt;math&amp;gt;(c, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, где вектор &amp;lt;math&amp;gt;u = (1; k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
|proof=По условию теоремы известно, что &amp;lt;tex&amp;gt;[a-b, b-c] &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u) &amp;lt; (a, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; c_{x} + k \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; k \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и при этом &amp;lt;tex&amp;gt;(b, u) &amp;lt; (c, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; a_{x} + k \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; k \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = k&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Из доказанной теоремы и следует корректность алгоритма.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull|Выпуклая оболочка]]&lt;br /&gt;
&lt;br /&gt;
*[[:Динамическое_программирование|Динамическое программирование]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;br /&gt;
[[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>188.170.214.39</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%BF%D1%80%D0%BE%D1%84%D0%B8%D0%BB%D1%8E&amp;diff=64541</id>
		<title>Динамическое программирование по профилю</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%BF%D1%80%D0%BE%D1%84%D0%B8%D0%BB%D1%8E&amp;diff=64541"/>
				<updated>2018-03-21T12:18:29Z</updated>
		
		<summary type="html">&lt;p&gt;188.170.214.39: /* Пример */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Определение&lt;br /&gt;
|definition='''Динамическое программирование по профилю''' (англ. ''dynamic programming with profile'') {{---}} способ оптимизации перебора количества вариантов с помощью [[Динамическое программирование|динамического программирования]], когда одно из измерений небольшое.&lt;br /&gt;
}}&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition='''Профиль''' (англ. ''profile'') {{---}} один из столбцов (строк), удовлетворяющий условию задачи. Обычно используется в качестве состояния динамики.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Общие принципы ==&lt;br /&gt;
Обычно дана таблица и надо посчитать количество замощений этой таблицы некоторыми фигурами (замощение шахматной доски доминошками). Можно перебрать все варианты и выбрать из них удовлетворяющие условию. Но можно воспользоваться методом динамического программирования по профилю и сократить время по одной размерности до линейной. Затем пусть у нас есть правило по которому надо заполнить и для него нам надо &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; предыдущих линий. Тогда можно перебрать все замощения  длиной &amp;lt;tex&amp;gt;k\times n&amp;lt;/tex&amp;gt;. В итоге нужно заполнить данную таблицу этими замощениями. Получается, что если перебирать все варианты нам понадобиться &amp;lt;tex&amp;gt;O(a^{nm})&amp;lt;/tex&amp;gt; времени, а если перебирать только состояния и переходить по ним нам потребуется &amp;lt;tex&amp;gt;O(a^{kn}m)&amp;lt;/tex&amp;gt; времени (где &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; {{---}} количество способов замощения одной клетки).&lt;br /&gt;
&lt;br /&gt;
== '''Задача о замощении домино''' ==&lt;br /&gt;
==='''Условие'''===&lt;br /&gt;
Найти количество способов замостить таблицу &amp;lt;tex&amp;gt;n\times m&amp;lt;/tex&amp;gt; с помощью доминошек размерами  &amp;lt;tex&amp;gt;1\times 2,2\times 1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==='''Решение'''===&lt;br /&gt;
&lt;br /&gt;
[[Файл:Домино.png|270px|thumb|right|Переходы (1-правильный переход, 2,3-неправильные)]]&lt;br /&gt;
&lt;br /&gt;
Для удобства можно хранить профили в виде двоичных масок.&lt;br /&gt;
В качестве состояния динамики будем использовать профили размерами &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;. В этом профиле &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; будет означать, что домино лежит горизонтально и заканчивается на этом столбце, иначе &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;. Таких профилей будет &amp;lt;tex&amp;gt;2^n&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Теперь проверим из какого профиля в какой можно перейти.&lt;br /&gt;
&lt;br /&gt;
Из профиля &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; в профиль &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; можно перейти если выполняются условия:&lt;br /&gt;
&lt;br /&gt;
* Можно положить горизонтальные домино. То есть там где в &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; профиле стоит &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, в &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; профиле должен стоять &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* Можно доложить в оставшиеся клетки вертикальные домино. То есть оставшиеся &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; профиле должны образовывать четные подстроки.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;d[i][j] = 1&amp;lt;/tex&amp;gt; если из профиля &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; можно перейти в &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-ый, иначе &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть так же &amp;lt;tex&amp;gt;a[k][i]&amp;lt;/tex&amp;gt; {{---}} количество способов замощения первых &amp;lt;tex&amp;gt;k-1&amp;lt;/tex&amp;gt; столбцов и заканчивавшийся на &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ом профиле.&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;a[k][i]=\displaystyle \sum_{j=0}^{2^n -1} a[k-1][j]\cdot d[j][i]&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответом будет &amp;lt;tex&amp;gt; \sum a[m][i]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; {{---}} профиль, который может быть последним (т.е. все группы из &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; имеют четные размеры).&lt;br /&gt;
&lt;br /&gt;
==='''Реализация'''===&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;// n, m {{---}} размер таблицы &amp;lt;/font&amp;gt; &lt;br /&gt;
   '''for''' &amp;lt;tex&amp;gt;\mathtt{i} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
     '''for''' &amp;lt;tex&amp;gt;\mathtt{j} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''if''' можно перейти из &amp;lt;tex&amp;gt;\mathtt{i}&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;\mathtt{j}&amp;lt;/tex&amp;gt; профиль &lt;br /&gt;
            &amp;lt;tex&amp;gt;\mathtt{d}[\mathtt{i}][\mathtt{j}] = \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 	'''else''' &lt;br /&gt;
 	    &amp;lt;tex&amp;gt;\mathtt{d}[\mathtt{i}][\mathtt{j}] = \mathtt{0}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &amp;lt;tex&amp;gt;\mathtt{a}[\mathtt{0}][\mathtt{0}] = \mathtt{1}&amp;lt;/tex&amp;gt; &amp;lt;font color=green&amp;gt;// Так как мы можем начать только с профиля где все клетки 0 &amp;lt;/font&amp;gt; &lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;k = \mathtt{1}..\mathtt{m} - \mathtt{1} &amp;lt;/tex&amp;gt;&lt;br /&gt;
     '''for''' &amp;lt;tex&amp;gt;\mathtt{i} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''for''' &amp;lt;tex&amp;gt;\mathtt{j} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 	    &amp;lt;tex&amp;gt;\mathtt{a}[\mathtt{k}][\mathtt{i}] = \mathtt{a}[\mathtt{k}][\mathtt{i}] + \mathtt{a}[\mathtt{k} - \mathtt{1}][\mathtt{j}]  \cdot  \mathtt{d}[\mathtt{j}][\mathtt{i}]&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &amp;lt;tex&amp;gt;\mathtt{ans} = \mathtt{0}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;\mathtt{i} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
     '''if''' можно закончить &amp;lt;tex&amp;gt;\mathtt{i}&amp;lt;/tex&amp;gt; профилем&lt;br /&gt;
         &amp;lt;tex&amp;gt;\mathtt{ans} = \mathtt{ans} + \mathtt{a}[\mathtt{m} - \mathtt{1}][\mathtt{i}]&amp;lt;/tex&amp;gt;&lt;br /&gt;
 '''return''' &amp;lt;tex&amp;gt;\mathtt{ans}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''' Оценка сложности: '''&lt;br /&gt;
подсчет &amp;lt;tex&amp;gt;d - 2^{2n}&amp;lt;/tex&amp;gt; , и подсчет &amp;lt;tex&amp;gt;a - 2^{2n}m&amp;lt;/tex&amp;gt; в итоге &amp;lt;tex&amp;gt;O(2^{2n}m)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''' Оценка памяти: '''&lt;br /&gt;
&amp;lt;tex&amp;gt;O(2^{2n} + 2^{2n}m)&amp;lt;/tex&amp;gt;, так же можно заметить что в массиве &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; состояния нам нужно только &amp;lt;tex&amp;gt;k - 1&amp;lt;/tex&amp;gt; состояние, при такой реализации нужно будет &amp;lt;tex&amp;gt;O(2^{2n})&amp;lt;/tex&amp;gt;. Еще можно не считать массив &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, а просто каждый раз перепроверять можем ли мы перейти в это состояние в итоге потребуется &amp;lt;tex&amp;gt;O(2^{n + 1})&amp;lt;/tex&amp;gt; памяти, но нам потребуется больше времени &amp;lt;tex&amp;gt;2^{2n}mf(i,j)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;f(i,j)&amp;lt;/tex&amp;gt; время проверки возможности перехода из &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; равно &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; и тогда время получается &amp;lt;tex&amp;gt;O(2^{2n}mn)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== '''Задача о симпатичных узорах''' ==&lt;br /&gt;
==='''Условие'''===&lt;br /&gt;
Дана таблица &amp;lt;tex&amp;gt;n\times m&amp;lt;/tex&amp;gt;, каждая клетка которой может быть окрашена в один из двух&lt;br /&gt;
цветов: белый или черный. Симпатичным узором называется такая раскраска, при&lt;br /&gt;
которой не существует квадрата &amp;lt;tex&amp;gt;2\times 2&amp;lt;/tex&amp;gt;, в котором все клетки одного цвета. Требуется найти количество симпатичных узоров для соответствующей таблицы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Симпатичне узоры.png|240px|thumb|right|]]&lt;br /&gt;
&lt;br /&gt;
==='''Решение'''===&lt;br /&gt;
Для удобства можно хранить профиля в виде двоичных масок.&lt;br /&gt;
В качестве состояния динамики будем использовать профили размера &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;. В этом профиле &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; будет означать что клетка закрашена в черный цвет, и &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; если в белый.&lt;br /&gt;
Из профиля &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-ый можно перейти если выполнено условие:&lt;br /&gt;
* если поставить &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; профиль рядом, то не должно быть квадратов &amp;lt;tex&amp;gt;2\times 2&amp;lt;/tex&amp;gt; одного цвета&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;d[i][j] = 1&amp;lt;/tex&amp;gt; если из профиля &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; можно перейти в &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-ый, иначе &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть так же &amp;lt;tex&amp;gt;a[k][i]&amp;lt;/tex&amp;gt; {{---}} количество способов раскрашивания первые &amp;lt;tex&amp;gt;k-1&amp;lt;/tex&amp;gt; столбцов и заканчивавшийся на &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ом профиле.&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;a[k][i]=\displaystyle \sum_{j=0}^{2^n -1} a[k-1][j]\cdot d[j][i]&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответом будет &amp;lt;tex&amp;gt; \displaystyle \sum_{i=0}^{2^n -1} a[m][i]&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==='''Реализация'''===&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;// n, m {{---}} размер таблицы &amp;lt;/font&amp;gt;  &lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;\mathtt{i} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
     '''for''' &amp;lt;tex&amp;gt;\mathtt{j} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''if''' можно перейти из &amp;lt;tex&amp;gt;\mathtt{i}&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;\mathtt{j}&amp;lt;/tex&amp;gt; профиль &lt;br /&gt;
             &amp;lt;tex&amp;gt;\mathtt{d}[\mathtt{i}][\mathtt{j}]\ =\ \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 	'''else'''&lt;br /&gt;
 	    &amp;lt;tex&amp;gt;\mathtt{d}[\mathtt{i}][\mathtt{j}]\ =\ \mathtt{0}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;\mathtt{i} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{a}[0][\mathtt{i}]\ = \mathtt{1}&amp;lt;/tex&amp;gt; &amp;lt;font color=green &amp;gt;// Так как мы можем начать c любого профиля&amp;lt;/font&amp;gt;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;\mathtt{k} = \mathtt{1}.. \mathtt{m} - \mathtt{1} &amp;lt;/tex&amp;gt;&lt;br /&gt;
     '''for'''  &amp;lt;tex&amp;gt;\mathtt{i} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''for''' &amp;lt;tex&amp;gt;\mathtt{j} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 	     &amp;lt;tex&amp;gt;\mathtt{a}[\mathtt{k}][\mathtt{i}] = \mathtt{a}[\mathtt{k}][\mathtt{i}] + \mathtt{a}[\mathtt{k} - 1][\mathtt{j}] \cdot \mathtt{d}[\mathtt{j}][\mathtt{i}]&amp;lt;/tex&amp;gt;  &lt;br /&gt;
 &amp;lt;tex&amp;gt;\mathtt{ans} = \mathtt{0}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;\mathtt{i} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{ans} = \mathtt{ans} + \mathtt{a}[\mathtt{m} - \mathtt{1}][\mathtt{i}]&amp;lt;/tex&amp;gt; &amp;lt;font color=green&amp;gt;// Так как мы можем закончить любым профилем &amp;lt;/font&amp;gt;&lt;br /&gt;
 '''return''' &amp;lt;tex&amp;gt;\mathtt{ans}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''' Оценка сложности: '''&lt;br /&gt;
подсчет &amp;lt;tex&amp;gt;d - 2^{2n}&amp;lt;/tex&amp;gt; , и подсчет &amp;lt;tex&amp;gt;a - 2^{2n}m&amp;lt;/tex&amp;gt; в итоге &amp;lt;tex&amp;gt;O(2^{2n}m)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''' Оценка памяти: '''&lt;br /&gt;
&amp;lt;tex&amp;gt;O(2^{2n}+2^{2n}m)&amp;lt;/tex&amp;gt;, так же можно заметить что в массиве &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; состояния нам нужно только &amp;lt;tex&amp;gt;k-1&amp;lt;/tex&amp;gt; состояние, при такой реализации нужно будет &amp;lt;tex&amp;gt;O(2^{2n})&amp;lt;/tex&amp;gt;. Еще можно не считать массив &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, а просто каждый раз перепроверять можем ли мы перейти в это состояние в итоге потребуется &amp;lt;tex&amp;gt;O(2^{n + 1})&amp;lt;/tex&amp;gt; памяти, но нам потребуется больше времени &amp;lt;tex&amp;gt;2^{2n}mf(i,j)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;f(i,j)&amp;lt;/tex&amp;gt; время проверки возможности перехода из &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; равно &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; и тогда время получается &amp;lt;tex&amp;gt;O(2^{2n}mn)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Динамика по изломанному профилю ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition='''Изломанный профиль''' (англ. ''broken profile'') {{---}} обобщение прямого профиля на случай, когда обработанным является не целое число столбцов, а некоторое количество столбцов и несколько первых клеток следующего столбца.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Это очень сильная оптимизация динамики по профилю. Идея в том, чтобы добиться как можно меньшего числа переходов (от одного профиля к другому).&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Еще раз используем в качестве примера задачу о замощении. Базовая линия теперь будет ломаной: при прохождении через &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю вертикаль сверху вниз, она переходит на предыдущую вертикаль и спускается до низу. Теперь профиль — это не только маска, но ещё и место излома.&lt;br /&gt;
[[Файл:img34.gif|300px|thumb|right|]]&lt;br /&gt;
Профилем будет пара &amp;lt;tex&amp;gt;(p, i)&amp;lt;/tex&amp;gt;, в &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; будет информация о &amp;lt;tex&amp;gt;n + 1&amp;lt;/tex&amp;gt; маленьком квадратике слева от базовой линии, имеющем с ней общие точки; &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; обозначает номер горизонтали, на которой произошел излом. Квадратики профиля будут нумероваться сверху вниз, так что угловой будет иметь номер &amp;lt;tex&amp;gt;i + 1&amp;lt;/tex&amp;gt;. Горизонтали будем нумеровать с нуля, так что &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; пробегает значения от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;n - 1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;d[pr_1][pr_2] = 1&amp;lt;/tex&amp;gt; если из профиля &amp;lt;tex&amp;gt;pr_1&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;(p_1, i_1)&amp;lt;/tex&amp;gt; можно перейти в &amp;lt;tex&amp;gt;pr_2 = (p_2, i_2)&amp;lt;/tex&amp;gt;, иначе &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* Eсли &amp;lt;tex&amp;gt;i &amp;lt; n - 1&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;i_1 + 1 = i_2&amp;lt;/tex&amp;gt;, иначе &amp;lt;tex&amp;gt;i_2 = 0 &amp;lt;/tex&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
* Mожно так положить доминошку, накрывающую квадратик с номером &amp;lt;tex&amp;gt;i + 1&amp;lt;/tex&amp;gt;, что после этого в &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt; будет храниться в точности информация о соответствующих квадратиках.&lt;br /&gt;
Проще говоря, доминошку можно класть только двумя способами {{---}} как показано на рисунках (на квадратик с номером &amp;lt;tex&amp;gt;i + 1&amp;lt;/tex&amp;gt; можно положить не более одной вертикальной и горизонтальной доминошки). То, что потом получается после сдвига вниз излома, и будет новым профилем. Заметим, что если  клетка &amp;lt;tex&amp;gt;i + 1&amp;lt;/tex&amp;gt; занята, то доминошку уже не надо класть, и &amp;lt;tex&amp;gt;(p, i)&amp;lt;/tex&amp;gt; логично отождествить с &amp;lt;tex&amp;gt;(p, i + 1)&amp;lt;/tex&amp;gt;. Условно пишется  {{---}} &amp;quot;&amp;lt;tex&amp;gt;i + 1&amp;lt;/tex&amp;gt;&amp;quot;, однако, нужно всегда иметь в виду возможность &amp;lt;tex&amp;gt;i = n - 1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Легко заметить, что количество профилей увеличилось в &amp;lt;tex&amp;gt;2n&amp;lt;/tex&amp;gt; раз (добавилось число от &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; и еще один бит). Но зато количество переходов резко сократилось с &amp;lt;tex&amp;gt;2^n&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;//Для профиля (p, i) выводятся все переходы из него (нумерация горизонталей начинается с нуля и i = 0..n - 1)&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;// Функция bit(x,i), возвращающая единицу или ноль или i-й бит в двоичной записи числа x&amp;lt;/font&amp;gt;&lt;br /&gt;
 '''print_all_links'''(&amp;lt;tex&amp;gt;\mathtt{p}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\mathtt{i}&amp;lt;/tex&amp;gt;):&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt;\mathtt{bit}(\mathtt{p, \mathtt{i} + \mathtt{1}}) == \mathtt{0}&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''if''' &amp;lt;tex&amp;gt;\mathtt{i} == \mathtt{n} - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
             '''println'''&amp;lt;tex&amp;gt;((\mathtt{p} - (\mathtt{2} \verb|&amp;lt;&amp;lt;| \ \mathtt{i})) \verb|&amp;lt;&amp;lt;| \ \mathtt{1}&amp;lt;/tex&amp;gt;, &amp;quot; &amp;quot;, &amp;lt;tex&amp;gt;\mathtt{0})&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''else'''&lt;br /&gt;
             '''println'''&amp;lt;tex&amp;gt;(\mathtt{p} - (\mathtt{2} \verb|&amp;lt;&amp;lt;| \ \mathtt{i})&amp;lt;/tex&amp;gt;, &amp;quot; &amp;quot;, &amp;lt;tex&amp;gt;\mathtt{i} + \mathtt{1})&amp;lt;/tex&amp;gt;&lt;br /&gt;
     '''else''' &lt;br /&gt;
         '''if''' &amp;lt;tex&amp;gt;\mathtt{bit}(\mathtt{p}, \mathtt{i}) == \mathtt{0}&amp;lt;/tex&amp;gt;&lt;br /&gt;
             '''if''' &amp;lt;tex&amp;gt;\mathtt{i} == \mathtt{n} - \mathtt{1} &amp;lt;/tex&amp;gt;&lt;br /&gt;
                '''println'''&amp;lt;tex&amp;gt;((\mathtt{p} \verb|&amp;lt;&amp;lt;| \ \mathtt{1})&amp;lt;/tex&amp;gt;, &amp;quot; &amp;quot;, &amp;lt;tex&amp;gt;\mathtt{0})&amp;lt;/tex&amp;gt;&lt;br /&gt;
             '''else'''   &lt;br /&gt;
                '''println'''&amp;lt;tex&amp;gt;(\mathtt{p} + (\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n})&amp;lt;/tex&amp;gt;, &amp;quot; &amp;quot;, &amp;lt;tex&amp;gt;(\mathtt{i} + \mathtt{1}) % \mathtt{n})&amp;lt;/tex&amp;gt;&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt;\mathtt{i} &amp;lt; \mathtt{n} - \mathtt{1}&amp;lt;/tex&amp;gt; &amp;amp;&amp;amp; &amp;lt;tex&amp;gt;\mathtt{bit}(\mathtt{p, \mathtt{i} + \mathtt{2}}) == \mathtt{0}&amp;lt;/tex&amp;gt;&lt;br /&gt;
             '''println'''&amp;lt;tex&amp;gt;(\mathtt{p} + (\mathtt{4} \verb|&amp;lt;&amp;lt;| \ \mathtt{i})&amp;lt;/tex&amp;gt;, &amp;quot; &amp;quot;, &amp;lt;tex&amp;gt;\mathtt{i} + \mathtt{1})&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:ok.jpg|640px|thumb|left|Возможные переходы]]&lt;br /&gt;
&lt;br /&gt;
При такой реализации существует немало профилей только с одним переходом (например, у которых &amp;lt;tex&amp;gt;(i + 1)&amp;lt;/tex&amp;gt;-й бит равен единице).&lt;br /&gt;
Отождествим все профили с один переходом с теми, кто их них получается. Это будет выглядеть так: пусть &amp;lt;tex&amp;gt;pr_2&amp;lt;/tex&amp;gt; (и только он) получается из &amp;lt;tex&amp;gt;pr_1&amp;lt;/tex&amp;gt;, который, в свою очередь, получается из &amp;lt;tex&amp;gt;pr_0&amp;lt;/tex&amp;gt;. Тогда имеются такие соотношения: &amp;lt;tex&amp;gt;d[pr_0, pr_1] = 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;d[pr_1, pr_2] = 1&amp;lt;/tex&amp;gt;. Отождествить &amp;lt;tex&amp;gt;pr_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;pr_2&amp;lt;/tex&amp;gt; {{---}} это, по сути, заменить эти два соотношение на одно, то есть теперь &amp;lt;tex&amp;gt;d[pr_0, pr_1] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;d[pr_1, pr_2] = 0&amp;lt;/tex&amp;gt;, но &amp;lt;tex&amp;gt;d[pr_0, pr_2] = 1&amp;lt;/tex&amp;gt;, и так далее.&lt;br /&gt;
&lt;br /&gt;
Таким образом, возможно сокращение профилей не менее чем вдвое. Хотя можно совершить дальнейшие оптимизации.&lt;br /&gt;
 &lt;br /&gt;
В итоге асимптотика составляет &amp;lt;tex&amp;gt;O(2^nnm)&amp;lt;/tex&amp;gt;. Это доказывает, что данный метод значительно лучше простого способа подсчёта динамики.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
*[[Динамическое программирование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
*[http://informatics.mccme.ru/moodle/file.php/9/dyn_prof.pdf Динамическое программирование по профилю]&lt;br /&gt;
*[http://informatics.mccme.ru/mod/book/view.php?id=290&amp;amp;chapterid=78 Динамическое программирование по изломанному профилю]&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;/div&gt;</summary>
		<author><name>188.170.214.39</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%BF%D1%80%D0%BE%D1%84%D0%B8%D0%BB%D1%8E&amp;diff=64540</id>
		<title>Динамическое программирование по профилю</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D0%BE_%D0%BF%D1%80%D0%BE%D1%84%D0%B8%D0%BB%D1%8E&amp;diff=64540"/>
				<updated>2018-03-21T11:32:32Z</updated>
		
		<summary type="html">&lt;p&gt;188.170.214.39: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Определение&lt;br /&gt;
|definition='''Динамическое программирование по профилю''' (англ. ''dynamic programming with profile'') {{---}} способ оптимизации перебора количества вариантов с помощью [[Динамическое программирование|динамического программирования]], когда одно из измерений небольшое.&lt;br /&gt;
}}&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition='''Профиль''' (англ. ''profile'') {{---}} один из столбцов (строк), удовлетворяющий условию задачи. Обычно используется в качестве состояния динамики.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Общие принципы ==&lt;br /&gt;
Обычно дана таблица и надо посчитать количество замощений этой таблицы некоторыми фигурами (замощение шахматной доски доминошками). Можно перебрать все варианты и выбрать из них удовлетворяющие условию. Но можно воспользоваться методом динамического программирования по профилю и сократить время по одной размерности до линейной. Затем пусть у нас есть правило по которому надо заполнить и для него нам надо &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; предыдущих линий. Тогда можно перебрать все замощения  длиной &amp;lt;tex&amp;gt;k\times n&amp;lt;/tex&amp;gt;. В итоге нужно заполнить данную таблицу этими замощениями. Получается, что если перебирать все варианты нам понадобиться &amp;lt;tex&amp;gt;O(a^{nm})&amp;lt;/tex&amp;gt; времени, а если перебирать только состояния и переходить по ним нам потребуется &amp;lt;tex&amp;gt;O(a^{kn}m)&amp;lt;/tex&amp;gt; времени (где &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; {{---}} количество способов замощения одной клетки).&lt;br /&gt;
&lt;br /&gt;
== '''Задача о замощении домино''' ==&lt;br /&gt;
==='''Условие'''===&lt;br /&gt;
Найти количество способов замостить таблицу &amp;lt;tex&amp;gt;n\times m&amp;lt;/tex&amp;gt; с помощью доминошек размерами  &amp;lt;tex&amp;gt;1\times 2,2\times 1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==='''Решение'''===&lt;br /&gt;
&lt;br /&gt;
[[Файл:Домино.png|270px|thumb|right|Переходы (1-правильный переход, 2,3-неправильные)]]&lt;br /&gt;
&lt;br /&gt;
Для удобства можно хранить профили в виде двоичных масок.&lt;br /&gt;
В качестве состояния динамики будем использовать профили размерами &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;. В этом профиле &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; будет означать, что домино лежит горизонтально и заканчивается на этом столбце, иначе &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;. Таких профилей будет &amp;lt;tex&amp;gt;2^n&amp;lt;/tex&amp;gt;.&lt;br /&gt;
Теперь проверим из какого профиля в какой можно перейти.&lt;br /&gt;
&lt;br /&gt;
Из профиля &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; в профиль &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; можно перейти если выполняются условия:&lt;br /&gt;
&lt;br /&gt;
* Можно положить горизонтальные домино. То есть там где в &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; профиле стоит &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt;, в &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; профиле должен стоять &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* Можно доложить в оставшиеся клетки вертикальные домино. То есть оставшиеся &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; профиле должны образовывать четные подстроки.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;d[i][j] = 1&amp;lt;/tex&amp;gt; если из профиля &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; можно перейти в &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-ый, иначе &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть так же &amp;lt;tex&amp;gt;a[k][i]&amp;lt;/tex&amp;gt; {{---}} количество способов замощения первых &amp;lt;tex&amp;gt;k-1&amp;lt;/tex&amp;gt; столбцов и заканчивавшийся на &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ом профиле.&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;a[k][i]=\displaystyle \sum_{j=0}^{2^n -1} a[k-1][j]\cdot d[j][i]&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответом будет &amp;lt;tex&amp;gt; \sum a[m][i]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; {{---}} профиль, который может быть последним (т.е. все группы из &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; имеют четные размеры).&lt;br /&gt;
&lt;br /&gt;
==='''Реализация'''===&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;// n, m {{---}} размер таблицы &amp;lt;/font&amp;gt; &lt;br /&gt;
   '''for''' &amp;lt;tex&amp;gt;\mathtt{i} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
     '''for''' &amp;lt;tex&amp;gt;\mathtt{j} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''if''' можно перейти из &amp;lt;tex&amp;gt;\mathtt{i}&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;\mathtt{j}&amp;lt;/tex&amp;gt; профиль &lt;br /&gt;
            &amp;lt;tex&amp;gt;\mathtt{d}[\mathtt{i}][\mathtt{j}] = \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 	'''else''' &lt;br /&gt;
 	    &amp;lt;tex&amp;gt;\mathtt{d}[\mathtt{i}][\mathtt{j}] = \mathtt{0}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &amp;lt;tex&amp;gt;\mathtt{a}[\mathtt{0}][\mathtt{0}] = \mathtt{1}&amp;lt;/tex&amp;gt; &amp;lt;font color=green&amp;gt;// Так как мы можем начать только с профиля где все клетки 0 &amp;lt;/font&amp;gt; &lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;k = \mathtt{1}..\mathtt{m} - \mathtt{1} &amp;lt;/tex&amp;gt;&lt;br /&gt;
     '''for''' &amp;lt;tex&amp;gt;\mathtt{i} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''for''' &amp;lt;tex&amp;gt;\mathtt{j} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 	    &amp;lt;tex&amp;gt;\mathtt{a}[\mathtt{k}][\mathtt{i}] = \mathtt{a}[\mathtt{k}][\mathtt{i}] + \mathtt{a}[\mathtt{k} - \mathtt{1}][\mathtt{j}]  \cdot  \mathtt{d}[\mathtt{j}][\mathtt{i}]&amp;lt;/tex&amp;gt;&lt;br /&gt;
 &amp;lt;tex&amp;gt;\mathtt{ans} = \mathtt{0}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;\mathtt{i} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
     '''if''' можно закончить &amp;lt;tex&amp;gt;\mathtt{i}&amp;lt;/tex&amp;gt; профилем&lt;br /&gt;
         &amp;lt;tex&amp;gt;\mathtt{ans} = \mathtt{ans} + \mathtt{a}[\mathtt{m} - \mathtt{1}][\mathtt{i}]&amp;lt;/tex&amp;gt;&lt;br /&gt;
 '''return''' &amp;lt;tex&amp;gt;\mathtt{ans}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''' Оценка сложности: '''&lt;br /&gt;
подсчет &amp;lt;tex&amp;gt;d - 2^{2n}&amp;lt;/tex&amp;gt; , и подсчет &amp;lt;tex&amp;gt;a - 2^{2n}m&amp;lt;/tex&amp;gt; в итоге &amp;lt;tex&amp;gt;O(2^{2n}m)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''' Оценка памяти: '''&lt;br /&gt;
&amp;lt;tex&amp;gt;O(2^{2n} + 2^{2n}m)&amp;lt;/tex&amp;gt;, так же можно заметить что в массиве &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; состояния нам нужно только &amp;lt;tex&amp;gt;k - 1&amp;lt;/tex&amp;gt; состояние, при такой реализации нужно будет &amp;lt;tex&amp;gt;O(2^{2n})&amp;lt;/tex&amp;gt;. Еще можно не считать массив &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, а просто каждый раз перепроверять можем ли мы перейти в это состояние в итоге потребуется &amp;lt;tex&amp;gt;O(2^{n + 1})&amp;lt;/tex&amp;gt; памяти, но нам потребуется больше времени &amp;lt;tex&amp;gt;2^{2n}mf(i,j)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;f(i,j)&amp;lt;/tex&amp;gt; время проверки возможности перехода из &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; равно &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; и тогда время получается &amp;lt;tex&amp;gt;O(2^{2n}mn)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== '''Задача о симпатичных узорах''' ==&lt;br /&gt;
==='''Условие'''===&lt;br /&gt;
Дана таблица &amp;lt;tex&amp;gt;n\times m&amp;lt;/tex&amp;gt;, каждая клетка которой может быть окрашена в один из двух&lt;br /&gt;
цветов: белый или черный. Симпатичным узором называется такая раскраска, при&lt;br /&gt;
которой не существует квадрата &amp;lt;tex&amp;gt;2\times 2&amp;lt;/tex&amp;gt;, в котором все клетки одного цвета. Требуется найти количество симпатичных узоров для соответствующей таблицы.&lt;br /&gt;
&lt;br /&gt;
[[Файл:Симпатичне узоры.png|240px|thumb|right|]]&lt;br /&gt;
&lt;br /&gt;
==='''Решение'''===&lt;br /&gt;
Для удобства можно хранить профиля в виде двоичных масок.&lt;br /&gt;
В качестве состояния динамики будем использовать профили размера &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;. В этом профиле &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; будет означать что клетка закрашена в черный цвет, и &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; если в белый.&lt;br /&gt;
Из профиля &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-ый можно перейти если выполнено условие:&lt;br /&gt;
* если поставить &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; профиль рядом, то не должно быть квадратов &amp;lt;tex&amp;gt;2\times 2&amp;lt;/tex&amp;gt; одного цвета&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;d[i][j] = 1&amp;lt;/tex&amp;gt; если из профиля &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; можно перейти в &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-ый, иначе &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть так же &amp;lt;tex&amp;gt;a[k][i]&amp;lt;/tex&amp;gt; {{---}} количество способов раскрашивания первые &amp;lt;tex&amp;gt;k-1&amp;lt;/tex&amp;gt; столбцов и заканчивавшийся на &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ом профиле.&lt;br /&gt;
Тогда &amp;lt;tex&amp;gt;a[k][i]=\displaystyle \sum_{j=0}^{2^n -1} a[k-1][j]\cdot d[j][i]&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ответом будет &amp;lt;tex&amp;gt; \displaystyle \sum_{i=0}^{2^n -1} a[m][i]&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==='''Реализация'''===&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;// n, m {{---}} размер таблицы &amp;lt;/font&amp;gt;  &lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;\mathtt{i} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
     '''for''' &amp;lt;tex&amp;gt;\mathtt{j} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''if''' можно перейти из &amp;lt;tex&amp;gt;\mathtt{i}&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;\mathtt{j}&amp;lt;/tex&amp;gt; профиль &lt;br /&gt;
             &amp;lt;tex&amp;gt;\mathtt{d}[\mathtt{i}][\mathtt{j}]\ =\ \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 	'''else'''&lt;br /&gt;
 	    &amp;lt;tex&amp;gt;\mathtt{d}[\mathtt{i}][\mathtt{j}]\ =\ \mathtt{0}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;\mathtt{i} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{a}[0][\mathtt{i}]\ = \mathtt{1}&amp;lt;/tex&amp;gt; &amp;lt;font color=green &amp;gt;// Так как мы можем начать c любого профиля&amp;lt;/font&amp;gt;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;\mathtt{k} = \mathtt{1}.. \mathtt{m} - \mathtt{1} &amp;lt;/tex&amp;gt;&lt;br /&gt;
     '''for'''  &amp;lt;tex&amp;gt;\mathtt{i} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''for''' &amp;lt;tex&amp;gt;\mathtt{j} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 	     &amp;lt;tex&amp;gt;\mathtt{a}[\mathtt{k}][\mathtt{i}] = \mathtt{a}[\mathtt{k}][\mathtt{i}] + \mathtt{a}[\mathtt{k} - 1][\mathtt{j}] \cdot \mathtt{d}[\mathtt{j}][\mathtt{i}]&amp;lt;/tex&amp;gt;  &lt;br /&gt;
 &amp;lt;tex&amp;gt;\mathtt{ans} = \mathtt{0}&amp;lt;/tex&amp;gt;&lt;br /&gt;
 '''for''' &amp;lt;tex&amp;gt;\mathtt{i} = \mathtt{0}..(\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n}) - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
     &amp;lt;tex&amp;gt;\mathtt{ans} = \mathtt{ans} + \mathtt{a}[\mathtt{m} - \mathtt{1}][\mathtt{i}]&amp;lt;/tex&amp;gt; &amp;lt;font color=green&amp;gt;// Так как мы можем закончить любым профилем &amp;lt;/font&amp;gt;&lt;br /&gt;
 '''return''' &amp;lt;tex&amp;gt;\mathtt{ans}&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''' Оценка сложности: '''&lt;br /&gt;
подсчет &amp;lt;tex&amp;gt;d - 2^{2n}&amp;lt;/tex&amp;gt; , и подсчет &amp;lt;tex&amp;gt;a - 2^{2n}m&amp;lt;/tex&amp;gt; в итоге &amp;lt;tex&amp;gt;O(2^{2n}m)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
''' Оценка памяти: '''&lt;br /&gt;
&amp;lt;tex&amp;gt;O(2^{2n}+2^{2n}m)&amp;lt;/tex&amp;gt;, так же можно заметить что в массиве &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; для &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt; состояния нам нужно только &amp;lt;tex&amp;gt;k-1&amp;lt;/tex&amp;gt; состояние, при такой реализации нужно будет &amp;lt;tex&amp;gt;O(2^{2n})&amp;lt;/tex&amp;gt;. Еще можно не считать массив &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt;, а просто каждый раз перепроверять можем ли мы перейти в это состояние в итоге потребуется &amp;lt;tex&amp;gt;O(2^{n + 1})&amp;lt;/tex&amp;gt; памяти, но нам потребуется больше времени &amp;lt;tex&amp;gt;2^{2n}mf(i,j)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;f(i,j)&amp;lt;/tex&amp;gt; время проверки возможности перехода из &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; равно &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; и тогда время получается &amp;lt;tex&amp;gt;O(2^{2n}mn)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Динамика по изломанному профилю ==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition='''Изломанный профиль''' (англ. ''broken profile'') {{---}} обобщение прямого профиля на случай, когда обработанным является не целое число столбцов, а некоторое количество столбцов и несколько первых клеток следующего столбца.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Это очень сильная оптимизация динамики по профилю. Идея в том, чтобы добиться как можно меньшего числа переходов (от одного профиля к другому).&lt;br /&gt;
&lt;br /&gt;
=== Пример ===&lt;br /&gt;
Еще раз используем в качестве примера задачу о замощении. Базовая линия теперь будет ломаной: при прохождении через &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю вертикаль сверху вниз, она переходит на предыдущую вертикаль и спускается до низу. Теперь профиль — это не только маска, но ещё и место излома.&lt;br /&gt;
[[Файл:img34.gif|300px|thumb|right|]]&lt;br /&gt;
Профилем будет пара &amp;lt;tex&amp;gt;(p, i)&amp;lt;/tex&amp;gt;, в &amp;lt;tex&amp;gt;p&amp;lt;/tex&amp;gt; будет информация о &amp;lt;tex&amp;gt;n + 1&amp;lt;/tex&amp;gt; маленьком квадратике слева от базовой линии, имеющем с ней общие точки; &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; обозначает номер горизонтали, на которой произошел излом. Квадратики профиля будут нумероваться сверху вниз, так что угловой будет иметь номер &amp;lt;tex&amp;gt;i + 1&amp;lt;/tex&amp;gt;. Горизонтали будем нумеровать с нуля, так что &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; пробегает значения от &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;n - 1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;d[i][j] = 1&amp;lt;/tex&amp;gt; если из профиля &amp;lt;tex&amp;gt;pr_1&amp;lt;/tex&amp;gt; = &amp;lt;tex&amp;gt;(p_1, i_1)&amp;lt;/tex&amp;gt; можно перейти в &amp;lt;tex&amp;gt;pr_2 = (p_2, i_2)&amp;lt;/tex&amp;gt;, иначе &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* Eсли &amp;lt;tex&amp;gt;i &amp;lt; n - 1&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;i_1 + 1 = i_2&amp;lt;/tex&amp;gt;, иначе &amp;lt;tex&amp;gt;i_2 = 0 &amp;lt;/tex&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
* Mожно так положить доминошку, накрывающую квадратик с номером &amp;lt;tex&amp;gt;i + 1&amp;lt;/tex&amp;gt;, что после этого в &amp;lt;tex&amp;gt;p_2&amp;lt;/tex&amp;gt; будет храниться в точности информация о соответствующих квадратиках.&lt;br /&gt;
Проще говоря, доминошку можно класть только двумя способами {{---}} как показано на рисунках (на квадратик с номером &amp;lt;tex&amp;gt;i + 1&amp;lt;/tex&amp;gt; можно положить не более одной вертикальной и горизонтальной доминошки). То, что потом получается после сдвига вниз излома, и будет новым профилем. Заметим, что если  клетка &amp;lt;tex&amp;gt;i + 1&amp;lt;/tex&amp;gt; занята, то доминошку уже не надо класть, и &amp;lt;tex&amp;gt;(p, i)&amp;lt;/tex&amp;gt; логично отождествить с &amp;lt;tex&amp;gt;(p, i + 1)&amp;lt;/tex&amp;gt;. Условно пишется  {{---}} &amp;quot;&amp;lt;tex&amp;gt;i + 1&amp;lt;/tex&amp;gt;&amp;quot;, однако, нужно всегда иметь в виду возможность &amp;lt;tex&amp;gt;i = n - 1&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Легко заметить, что количество профилей увеличилось в &amp;lt;tex&amp;gt;2n&amp;lt;/tex&amp;gt; раз (добавилось число от &amp;lt;tex&amp;gt;1&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; и еще один бит). Но зато количество переходов резко сократилось с &amp;lt;tex&amp;gt;2^n&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;2&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;//Для профиля (p, i) выводятся все переходы из него (нумерация горизонталей начинается с нуля и i = 0..n - 1)&amp;lt;/font&amp;gt;&lt;br /&gt;
 &amp;lt;font color=green&amp;gt;// Функция bit(x,i), возвращающая единицу или ноль или i-й бит в двоичной записи числа x&amp;lt;/font&amp;gt;&lt;br /&gt;
 '''print_all_links'''(&amp;lt;tex&amp;gt;\mathtt{p}&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;\mathtt{i}&amp;lt;/tex&amp;gt;):&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt;\mathtt{bit}(\mathtt{p, \mathtt{i} + \mathtt{1}}) == \mathtt{0}&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''if''' &amp;lt;tex&amp;gt;\mathtt{i} == \mathtt{n} - \mathtt{1}&amp;lt;/tex&amp;gt;&lt;br /&gt;
             '''println'''&amp;lt;tex&amp;gt;((\mathtt{p} - (\mathtt{2} \verb|&amp;lt;&amp;lt;| \ \mathtt{i})) \verb|&amp;lt;&amp;lt;| \ \mathtt{1}&amp;lt;/tex&amp;gt;, &amp;quot; &amp;quot;, &amp;lt;tex&amp;gt;\mathtt{0})&amp;lt;/tex&amp;gt;&lt;br /&gt;
         '''else'''&lt;br /&gt;
             '''println'''&amp;lt;tex&amp;gt;(\mathtt{p} - (\mathtt{2} \verb|&amp;lt;&amp;lt;| \ \mathtt{i})&amp;lt;/tex&amp;gt;, &amp;quot; &amp;quot;, &amp;lt;tex&amp;gt;\mathtt{i} + \mathtt{1})&amp;lt;/tex&amp;gt;&lt;br /&gt;
     '''else''' &lt;br /&gt;
         '''if''' &amp;lt;tex&amp;gt;\mathtt{bit}(\mathtt{p}, \mathtt{i}) == \mathtt{0}&amp;lt;/tex&amp;gt;&lt;br /&gt;
             '''if''' &amp;lt;tex&amp;gt;\mathtt{i} == \mathtt{n} - \mathtt{1} &amp;lt;/tex&amp;gt;&lt;br /&gt;
                '''println'''&amp;lt;tex&amp;gt;((\mathtt{p} \verb|&amp;lt;&amp;lt;| \ \mathtt{1})&amp;lt;/tex&amp;gt;, &amp;quot; &amp;quot;, &amp;lt;tex&amp;gt;\mathtt{0})&amp;lt;/tex&amp;gt;&lt;br /&gt;
             '''else'''   &lt;br /&gt;
                '''println'''&amp;lt;tex&amp;gt;(\mathtt{p} + (\mathtt{1} \verb|&amp;lt;&amp;lt;| \ \mathtt{n})&amp;lt;/tex&amp;gt;, &amp;quot; &amp;quot;, &amp;lt;tex&amp;gt;(\mathtt{i} + \mathtt{1}) % \mathtt{n})&amp;lt;/tex&amp;gt;&lt;br /&gt;
     '''if''' &amp;lt;tex&amp;gt;\mathtt{i} &amp;lt; \mathtt{n} - \mathtt{1}&amp;lt;/tex&amp;gt; &amp;amp;&amp;amp; &amp;lt;tex&amp;gt;\mathtt{bit}(\mathtt{p, \mathtt{i} + \mathtt{2}}) == \mathtt{0}&amp;lt;/tex&amp;gt;&lt;br /&gt;
             '''println'''&amp;lt;tex&amp;gt;(\mathtt{p} + (\mathtt{4} \verb|&amp;lt;&amp;lt;| \ \mathtt{i})&amp;lt;/tex&amp;gt;, &amp;quot; &amp;quot;, &amp;lt;tex&amp;gt;\mathtt{i} + \mathtt{1})&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:ok.jpg|640px|thumb|left|Возможные переходы]]&lt;br /&gt;
&lt;br /&gt;
При такой реализации существует немало профилей только с одним переходом (например, у которых &amp;lt;tex&amp;gt;(i + 1)&amp;lt;/tex&amp;gt;-й бит равен единице).&lt;br /&gt;
Отождествим все профили с один переходом с теми, кто их них получается. Это будет выглядеть так: пусть &amp;lt;tex&amp;gt;pr_2&amp;lt;/tex&amp;gt; (и только он) получается из &amp;lt;tex&amp;gt;pr_1&amp;lt;/tex&amp;gt;, который, в свою очередь, получается из &amp;lt;tex&amp;gt;pr_0&amp;lt;/tex&amp;gt;. Тогда имеются такие соотношения: &amp;lt;tex&amp;gt;d[pr_0, pr_1] = 1&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;d[pr_1, pr_2] = 1&amp;lt;/tex&amp;gt;. Отождествить &amp;lt;tex&amp;gt;pr_1&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;pr_2&amp;lt;/tex&amp;gt; {{---}} это, по сути, заменить эти два соотношение на одно, то есть теперь &amp;lt;tex&amp;gt;d[pr_0, pr_1] = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;d[pr_1, pr_2] = 0&amp;lt;/tex&amp;gt;, но &amp;lt;tex&amp;gt;d[pr_0, pr_2] = 1&amp;lt;/tex&amp;gt;, и так далее.&lt;br /&gt;
&lt;br /&gt;
Таким образом, возможно сокращение профилей не менее чем вдвое. Хотя можно совершить дальнейшие оптимизации.&lt;br /&gt;
 &lt;br /&gt;
В итоге асимптотика составляет &amp;lt;tex&amp;gt;O(2^nnm)&amp;lt;/tex&amp;gt;. Это доказывает, что данный метод значительно лучше простого способа подсчёта динамики.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
*[[Динамическое программирование]]&lt;br /&gt;
&lt;br /&gt;
== Источники информации ==&lt;br /&gt;
*[http://informatics.mccme.ru/moodle/file.php/9/dyn_prof.pdf Динамическое программирование по профилю]&lt;br /&gt;
*[http://informatics.mccme.ru/mod/book/view.php?id=290&amp;amp;chapterid=78 Динамическое программирование по изломанному профилю]&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;/div&gt;</summary>
		<author><name>188.170.214.39</name></author>	</entry>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=64138</id>
		<title>Convex hull trick</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Convex_hull_trick&amp;diff=64138"/>
				<updated>2018-03-11T05:50:02Z</updated>
		
		<summary type="html">&lt;p&gt;188.170.214.39: /* Наивное решение */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Convex hull trick {{---}} один из методов оптимизации [[Динамическое_программирование | динамического программирования]], использующий идею [[Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull|выпуклой оболочки]]. Позволяет улучшить ассимптотику решения некоторых задач, решемых методом динамического программирования, с &amp;lt;math&amp;gt;O(n^2)&amp;lt;/math&amp;gt; до &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt;. Техника впервые появилась в 1995 году (задачу на нее предложили в USACO {{---}} национальной олимпиаде США по программированию). Массовую известность получила после IOI (международной олимпиады по программированию для школьников) 2002.&lt;br /&gt;
&lt;br /&gt;
==Пример задачи, решаемой методом convex hull trick==&lt;br /&gt;
Рассмотрим задачу на ДП:&lt;br /&gt;
{{Задача&lt;br /&gt;
|definition = Есть &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt; деревьев с высотами &amp;lt;tex&amp;gt;a_1, a_2, \dots, a_n&amp;lt;/tex&amp;gt; (в метрах). Требуется спилить их все, потратив минимальное количество монет на заправку&lt;br /&gt;
бензопилы. Но пила устроена так, что она может спиливать только по &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; метру от дерева, к которому ее применили. Также после&lt;br /&gt;
срубленного метра (любого дерева) пилу нужно заправлять, платя за  бензин определенной кол-во монет. Причем стоимость &lt;br /&gt;
бензина зависит от срубленных (полностью) деревьев. Если сейчас максимальный индекс срубленного дерева равен &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, то цена заправки &lt;br /&gt;
равна &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt;.  Изначально пила заправлена.&lt;br /&gt;
Также известны следующие ограничения : &amp;lt;tex&amp;gt;c_n = 0, a_1 = 1, a_i&amp;lt;/tex&amp;gt; возрастают, &amp;lt;tex&amp;gt;c_i&amp;lt;/tex&amp;gt; убывают. Изначально пила заправлена.&lt;br /&gt;
(убывание и возрастание нестрогие)&lt;br /&gt;
}}&lt;br /&gt;
(Задача H с Санкт-Петербургских сборов к РОИ 2016 &amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf  Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;)&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;{{#if: {{{neat|}}}|&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #fcfcfc; float:left;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background-color: #ddd;&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;|&lt;br /&gt;
&amp;lt;table border=&amp;quot;0&amp;quot; width=&amp;quot;100%&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;background-color: #ddd&amp;quot;&amp;gt;'''Задача:'''&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;td style=&amp;quot;border:1px dashed #2f6fab; padding: 8px; background-color: #fcfcfc; font-style: italic;&amp;quot;&amp;gt;{{{definition}}}&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;}}&lt;br /&gt;
&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Наивное решение==&lt;br /&gt;
Сначала заметим важный факт : т.к. &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают (нестрого) и &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;, то все &amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; неотрицательны.&lt;br /&gt;
Понятно, что нужно затратив минимальную стоимость срубить последнее (&amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt;-е) дерево, т.к. после него все деревья можно будет рубить бесплатно (т.к. &amp;lt;tex&amp;gt;c[n] = 0&amp;lt;/tex&amp;gt;). Посчитаем следующую динамику : &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; {{---}} минимальная стоимость, заплатив которую можно добиться того, что дерево номер &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; будет срублено.&lt;br /&gt;
База динамики : &amp;lt;tex&amp;gt;dp[1] = 0&amp;lt;/tex&amp;gt;, т.к. изначально пила заправлена и высота первого дерева равна &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt;, по условию задачи.&lt;br /&gt;
Переход динамики :  понятно, что выгодно рубить сначала более дорогие и низкие деревья, а потом более высокие и дешевые (док-во этого факта оставляется читателям как несложное упражнение, т.к. эта идея относится скорее к теме [[Теорема_Радо-Эдмондса_(жадный_алгоритм)|жадных алгоритмов]], чем к теме данной статьи). Поэтому перед  &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы обязательно срубили какое-то &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем &amp;lt;tex&amp;gt;j \leqslant i - 1&amp;lt;/tex&amp;gt;. Поэтому чтобы найти &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt; нужно перебрать все &amp;lt;tex&amp;gt;1 \leqslant j \leqslant i - 1&amp;lt;/tex&amp;gt; и попытаться использовать ответ для дерева номер &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Итак, пусть перед &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-м деревом мы полностью срубили &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е, причем высота &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составляет &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt;, а т.к. последнее дерево, которое мы срубили, имеет индекс &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, то стоимость каждого метра &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева составит &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt;.  Поэтому на сруб &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева мы потратим &amp;lt;tex&amp;gt;a[i] \cdot c[j]&amp;lt;/tex&amp;gt; монет. Также не стоит забывать, что ситуацию, когда  &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;-е дерево полностью срублено, мы получили не бесплатно, а за &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; монет.&lt;br /&gt;
Итоговая формула пересчета : &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=1...i-1} (dp[j] + a[i] \cdot c[j])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Посмотрим на код вышеописанного решения:&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{simpleDP}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
    dp[1] = 0&lt;br /&gt;
    dp[2] = dp[3] = ... = dp[n] = &amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    '''for''' i = 2 = 1..n  &lt;br /&gt;
       dp[i] = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
       '''for''' j = 1..i - 1 &lt;br /&gt;
           '''if''' (dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j] &amp;lt; dp[i])&lt;br /&gt;
               dp[i] = dp[j] + a[i] &amp;lt;tex&amp;gt;\cdot&amp;lt;/tex&amp;gt; c[j]&lt;br /&gt;
 '''return''' dp[n]&lt;br /&gt;
Нетрудно видеть, что такая динамика работает за &amp;lt;tex&amp;gt;O(n^2)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Ключевая идея оптимизации==&lt;br /&gt;
Для начала сделаем замену обозначений. Давайте обозначим &amp;lt;tex&amp;gt;dp[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;b[j]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;a[i]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а &amp;lt;tex&amp;gt;c[j]&amp;lt;/tex&amp;gt; за &amp;lt;tex&amp;gt;k[j]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Теперь формула приняла вид &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(k[j] \cdot x[i] + b[j])&amp;lt;/tex&amp;gt;. Выражение &amp;lt;tex&amp;gt;k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt; {{---}} это в точности уравнение прямой вида &amp;lt;tex&amp;gt;y = kx + b&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Сопоставим каждому &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, обработанному ранее, прямую &amp;lt;tex&amp;gt;y[j](x) = k[j] \cdot x + b[j]&amp;lt;/tex&amp;gt;. Из условия «&amp;lt;tex&amp;gt;c[i]&amp;lt;/tex&amp;gt; убывают &amp;lt;tex&amp;gt;\Leftrightarrow  k[j]&amp;lt;/tex&amp;gt; уменьшаются с номером &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;» следует то, что прямые, полученные ранее отсортированы в порядке убывания углового коэффициент. Давайте нарисуем несколько таких прямых :&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture1convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Выделим множество точек &amp;lt;tex&amp;gt;(x_0, y_0)&amp;lt;/tex&amp;gt; , таких что все они принадлежат одной из прямых и при этом нету ни одной прямой &amp;lt;tex&amp;gt;y’(x)&amp;lt;/tex&amp;gt;, такой что &amp;lt;tex&amp;gt;y’(x_0) &amp;lt; y_0&amp;lt;/tex&amp;gt;. Иными словами возьмем «выпуклую (вверх) оболочку» нашего множества прямых (её еще называют нижней ошибающей множества прямых на плоскости). Назовем ее «&amp;lt;tex&amp;gt;y = convex(x)&amp;lt;/tex&amp;gt;». Видно, что множество точек &amp;lt;math&amp;gt;(x, convex(x))&amp;lt;/math&amp;gt; представляет собой выпуклую вверх функцию.&lt;br /&gt;
&lt;br /&gt;
==Цель нижней огибающей множества прямых==&lt;br /&gt;
Пусть мы считаем динамику для &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-го дерева. Его задает &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;. Итак, нам нужно для данного &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; найти &amp;lt;tex&amp;gt;\min\limits_{j=0..i-1}(k[j] \cdot x[i] + b[i]) = \min\limits_{j=0..i-1}(y[j](x[i]))&amp;lt;/tex&amp;gt;. Это выражение есть &amp;lt;math&amp;gt;convex(x[i])&amp;lt;/math&amp;gt;. Из монотонности угловых коэффицентов отрезков, задающих выпуклую оболочку, и их расположения по координатам x следует то, что отрезок, который пересекает прямую &amp;lt;tex&amp;gt;x = x[i]&amp;lt;/tex&amp;gt;, можно найти [[Целочисленный_двоичный_поиск|бинарным поиском]]. Это потребует &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени на поиск такого &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;dp[i] = k[j] \cdot x[i] + b[j]&amp;lt;/tex&amp;gt;. Теперь осталось научиться поддерживать множество прямых и быстро добавлять &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ю прямую после того, как мы посчитали &amp;lt;tex&amp;gt;b[i] = dp[i]&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Воспользуемся идеей алгоритма построения выпуклой оболочки множества точек. Заведем 2 стека &amp;lt;tex&amp;gt;k[]&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;b[]&amp;lt;/tex&amp;gt;, которые задают прямые в отсортированном порядке их угловыми коэффицентами и свободными членами. Рассмотрим ситуацию, когда мы хотим добавить новую (&amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-тую) прямую в множество. Пусть сейчас в множестве лежит &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt; прямых (нумерация с 1). Пусть &amp;lt;tex&amp;gt;(x_L, y_L)&amp;lt;/tex&amp;gt; {{---}} точка пересечения &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-й прямой множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й, а &amp;lt;tex&amp;gt;(x_R, y_R)&amp;lt;/tex&amp;gt; {{---}} точка пересечения новой прямой, которую мы хотим добавить в конец множества и &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-й. Нас будут интересовать только их &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-овые координаты &amp;lt;tex&amp;gt;x_L&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;x_R&amp;lt;/tex&amp;gt;, соответственно. Если оказалось, что новая прямая пересекает &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю прямую выпуклой оболочки позже, чем &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-я &amp;lt;tex&amp;gt;sz - 1&amp;lt;/tex&amp;gt;-ю, т.е. &amp;lt;tex&amp;gt;(x_L \geqslant x_R)&amp;lt;/tex&amp;gt;, то &amp;lt;tex&amp;gt;sz&amp;lt;/tex&amp;gt;-ю удалим из нашего множества, иначе - остановимся. Так будем делать, пока либо число прямых в стеке не станет равным 2, либо &amp;lt;tex&amp;gt;x_L&amp;lt;/tex&amp;gt; не станет меньше &amp;lt;tex&amp;gt;x_R.&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Асимптотика : аналогично обычному алгоритму построения выпуклой оболочки, каждая прямая ровно &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз добавится в стек и максимум &amp;lt;math&amp;gt;1&amp;lt;/math&amp;gt; раз удалится. Значит время работы перестройки выпуклой оболочки займет &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; суммарно.&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture2convexhull.png]]&lt;br /&gt;
[[Файл:picture3convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th1239. &lt;br /&gt;
|statement=Алгоритм построения нижней огибающей множества прямых корректен.&lt;br /&gt;
|proof=Достаточно показать, что последнюю прямую нужно удалить из множества &amp;lt;tex&amp;gt;\Leftrightarrow&amp;lt;/tex&amp;gt;, когда она наша новая прямая пересекает ее в точке с координатой по оси X, меньшей, чем последняя - предпоследнюю.&lt;br /&gt;
&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;Y(x) = Kx + B&amp;lt;/tex&amp;gt; {{---}} уравнение новой прямой,  &amp;lt;tex&amp;gt;y[i](x) = K[i]x + B[i]&amp;lt;/tex&amp;gt; {{---}} уравнения прямых множества. Тогда т.к. &amp;lt;tex&amp;gt;K &amp;lt; K[sz]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [- \infty; x_R]  : y[sz](x) &amp;lt;= Y(x)&amp;lt;/tex&amp;gt;, а т.к. &amp;lt;tex&amp;gt; K[sz] &amp;lt; K[sz - 1]&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [x_L; + \infty]  : y[sz - 1](x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;x_L &amp;lt; x_R&amp;lt;/tex&amp;gt;, то при &amp;lt;tex&amp;gt;x \in [x_L; x_R]  : y[sz - 1] \geqslant y[sz](x) и Y(x) \geqslant y[sz](x)&amp;lt;/tex&amp;gt;, т.е. на отрезке &amp;lt;tex&amp;gt;[x_L; x_R]&amp;lt;/tex&amp;gt; прямая номер sz лежит ниже остальных и её нужно оставить в множестве. Если же &amp;lt;tex&amp;gt;x_L &amp;gt; x_R&amp;lt;/tex&amp;gt;, то она ниже всех на отрезке &amp;lt;tex&amp;gt;[x_L; x_R] = \varnothing &amp;lt;/tex&amp;gt;, т.е. её можно удалить из множества.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Детали реализации:==&lt;br /&gt;
Будем хранить 2 массива : &amp;lt;tex&amp;gt;front[]&amp;lt;/tex&amp;gt; {{---}} &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt;-координаты, начиная с которых прямые совпадают с выпуклой оболочкой (т.е. i-я прямая совпадает с выпуклой оболочкой текущего множества прямых при &amp;lt;tex&amp;gt;x&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[front[i]; front[i + 1])&amp;lt;/tex&amp;gt; ) и &amp;lt;tex&amp;gt;st[]&amp;lt;/tex&amp;gt; {{---}} номера деревьев, соответствующих прямым (т.е. &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-я прямая множества, где &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[1; sz]&amp;lt;/tex&amp;gt; соответствует дереву номер &amp;lt;tex&amp;gt;sz[i]&amp;lt;/tex&amp;gt;). Также воспользуемся тем, что &amp;lt;tex&amp;gt;x[i] = a[i]&amp;lt;/tex&amp;gt; возрастают (по условию задачи), а значит мы можем искать первое такое &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, что &amp;lt;tex&amp;gt;x[i] \geqslant front[j]&amp;lt;/tex&amp;gt; не бинарным поиском, а методом двух указателей за &amp;lt;tex&amp;gt;O(n)&amp;lt;/tex&amp;gt; операций суммарно. Также массив &amp;lt;math&amp;gt;front[]&amp;lt;/math&amp;gt; можно хранить в целых числах, округляя х-координаты в сторону лежащих правее по оси x до ближайшего целого (*), т.к. на самом деле мы, считая динамику, подставляем в уравнения прямых только целые &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt;, а значит если &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;-я прямая пересекается с &amp;lt;tex&amp;gt;k+1&amp;lt;/tex&amp;gt;-й в точке &amp;lt;tex&amp;gt;z +&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt; (&amp;lt;math&amp;gt;z&amp;lt;/math&amp;gt;-целое,  &amp;lt;tex&amp;gt;\alpha&amp;lt;/tex&amp;gt;  &amp;lt;tex&amp;gt;\in&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;[0;1)&amp;lt;/tex&amp;gt;), то мы будем подставлять в их уравнения &amp;lt;tex&amp;gt;z&amp;lt;/tex&amp;gt;  или &amp;lt;tex&amp;gt;z + 1&amp;lt;/tex&amp;gt;. Поэтому можно считать, что новая прямая начинает совпадать с выпуклой оболочкой, начиная с &amp;lt;tex&amp;gt;x = z+1&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Реализация==&lt;br /&gt;
  '''int''' &amp;lt;tex&amp;gt;\mathtt{ConvexHullTrick}&amp;lt;/tex&amp;gt;('''int''' a[n], '''int''' c[n])&lt;br /&gt;
     st[1] = 1&lt;br /&gt;
     from[1] = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt;&amp;lt;font color=green&amp;gt;// первая прямая покрывает все x-ы, начиная с -∞ &amp;lt;/font&amp;gt;&lt;br /&gt;
     sz = 1 &amp;lt;font color=green&amp;gt;// текущий размер выпуклой оболочки &amp;lt;/font&amp;gt;&lt;br /&gt;
     pos = 1 &amp;lt;font color=green&amp;gt;// текущая позиция первого такого j, что x[i] \geqslant front[st[j]] &amp;lt;/font &amp;gt;&lt;br /&gt;
     '''for'''  i = 2..n  &lt;br /&gt;
          '''while''' (front[pos] &amp;lt; x[i]) &amp;lt;font color=green&amp;gt;// метод 1 указателя (ищем первое pos, такое что x[i] покрывается &amp;quot;областью действия&amp;quot; st[pos]-той прямой &amp;lt;/font &amp;gt;&lt;br /&gt;
               pos = pos + 1&lt;br /&gt;
          j = st[pos]&lt;br /&gt;
          dp[i] = K[j] &amp;lt;math&amp;gt;\cdot&amp;lt;/math&amp;gt; a[i] + B[j] &lt;br /&gt;
          '''if''' (i &amp;lt; n)   &amp;lt;font color=green&amp;gt;// если у нас добавляется НЕ последняя прямая, то придется пересчитать выпуклую оболочку &amp;lt;/font &amp;gt;&lt;br /&gt;
                K[i] = c[i]  &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                B[i] = dp[i] &amp;lt;font color=green&amp;gt;// наши переобозначения переменных &amp;lt;/font &amp;gt;&lt;br /&gt;
                x = -&amp;lt;tex&amp;gt;\infty&amp;lt;/tex&amp;gt; &lt;br /&gt;
                '''while''' ''true'' &lt;br /&gt;
                      j = st[sz]&lt;br /&gt;
                      x = divide(B[j] - B[i], K[i] - K[j]) &amp;lt;font color=green&amp;gt;// x-координата пересечения с последней прямой оболочки, округленное в нужную сторону (*) &amp;lt;/font &amp;gt;&lt;br /&gt;
                      '''if''' (x &amp;gt; from[sz]) '''break'''  &amp;lt;font color=green&amp;gt;// перестаем удалять последнюю прямую из множества, если новая прямая пересекает ее позже, чем начинается ее &amp;quot;область действия&amp;quot; &amp;lt;/font &amp;gt;&lt;br /&gt;
                      sz = sz - 1&amp;lt;font color=green&amp;gt;// удаляем последнюю прямую, если она лишняя &amp;lt;/font &amp;gt;&lt;br /&gt;
                 st[sz + 1] = i  &lt;br /&gt;
                 from[sz + 1] = x &amp;lt;font color=green&amp;gt;// добавили новую прямую &amp;lt;/font &amp;gt;&lt;br /&gt;
                 sz = sz + 1&lt;br /&gt;
  '''return''' dp[n] &lt;br /&gt;
Здесь функция &amp;lt;tex&amp;gt;\mathtt{divide(a, b)}&amp;lt;/tex&amp;gt; возвращает нужное(*) округление &amp;lt;tex&amp;gt;\frac{a}{b}&amp;lt;/tex&amp;gt;. Приведем её код :&lt;br /&gt;
 '''int''' &amp;lt;tex&amp;gt;\mathtt{divide}&amp;lt;/tex&amp;gt;('''int''' a, '''int''' b)&lt;br /&gt;
        delta = 0&lt;br /&gt;
        '''if''' (a '''mod''' b ≠ 0) delta = 1&lt;br /&gt;
        '''if''' ((a &amp;gt; 0 '''and''' b &amp;gt; 0) '''or''' (a &amp;lt; 0 '''and''' b &amp;lt; 0)) '''return''' [a / b] + delta&lt;br /&gt;
 '''return''' -[|a| / |b|]&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Такая реализация будет работать за &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Динамический convex hull trick==&lt;br /&gt;
	Заметим, что условия на возрастание/убывание &amp;lt;tex&amp;gt;k[i]&amp;lt;/tex&amp;gt; на убывание/возрастание и &amp;lt;tex&amp;gt;x[i]&amp;lt;/tex&amp;gt; выглядят достаточно редкими для большинства задач. Пусть в задаче таких ограничений нет. Первый способ борьбы с этой проблемой - отсортировать входные данные нужным образом, не испортив свойств задачи (пример : задача G c Санкт-Петербургских сборов к РОИ 2016 &amp;lt;ref&amp;gt;[http://neerc.ifmo.ru/school/camp-2016/problems/20160318a.pdf Сайт с задачами Санкт-Петербургских сборов к РОИ 2016]&amp;lt;/ref&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
	Но рассмотрим общий случай. По-прежнему у нас есть выпуклая оболочка прямых, с помощью которой мы за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; можем найти   &amp;lt;tex&amp;gt;dp[i]&amp;lt;/tex&amp;gt;, но теперь вставку &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-й прямой в оболочку уже нельзя выполнить описанным ранее способом за &amp;lt;tex&amp;gt;O(1)&amp;lt;/tex&amp;gt; в среднем. У нас есть выпуклая оболочка, наша прямая пересекает ее, возможно, «отсекая» несколько отрезков выпуклой оболочки в середине (рис. 4 : красная прямая - та, которую мы хотим вставить в наше множество). Более формально : теперь наша новая прямая будет ниже остальных при &amp;lt;tex&amp;gt;x \in [x_1; x_2]&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;x_1, x_2 \in R&amp;lt;/tex&amp;gt; - точки пересечения с некоторыми прямыми, причем &amp;lt;tex&amp;gt;x_2&amp;lt;/tex&amp;gt; не обязательно равно &amp;lt;tex&amp;gt;+ \infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
[[Файл:picture4convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Чтобы уметь вставлять прямую в множество будем хранить [[Красно-черное_дерево|двоичное дерево поиска]], в вершинах которого будут пары &amp;lt;tex&amp;gt;(k, st)&amp;lt;/tex&amp;gt; =  (коэффицент прямой, ее номер в глобальной нумерации). Когда приходит новая прямая, ищем последнюю прямую с меньшим угловым коэффицентом, чем у той прямой, которую мы хотим добавить в множество. Поиск такой прямой занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt;. Начиная с найденной прямой выполняем &amp;quot;старый&amp;quot; алгоритм (удаляем, пока текущая прямая множества бесполезна). И симметричный алгоритм применяем ко всем прямым справа от нашей (удаляем правого соседа нашей прямой, пока она пересекает нас позже, чем своего правого соседа).&lt;br /&gt;
	&lt;br /&gt;
Асимптотика решения составит &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; на каждый из &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; запросов «добавить прямую» + &amp;lt;tex&amp;gt;O(n\cdot\log(n))&amp;lt;/tex&amp;gt; суммарно на удаление прямых, т.к. по-прежнему каждая прямая не более одного раза удалится из множества, а каждое удаление из std::set занимает &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; времени. Итого &amp;lt;math&amp;gt;O(n\cdot\log(n))&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Альтернативный подход ==&lt;br /&gt;
Другой способ интерпретировать выражение &amp;lt;tex&amp;gt;dp[i] = \min\limits_{j=0...i-1}(c[j] \cdot a[i] + dp[j])&amp;lt;/tex&amp;gt;  заключается в том, что мы будем пытаться свести задачу к стандартной выпуклой оболочке множества точек. Перепишем выражение средующим образом : &amp;lt;tex&amp;gt;dp[j] + a[i] \cdot c[j] = (dp[j], c[j]) \cdot (1, a[i])&amp;lt;/tex&amp;gt;, т.е. запишем как скалярное произведение векторов &amp;lt;tex&amp;gt;v[j] = (dp[j], c[j])&amp;lt;/tex&amp;gt; и &amp;lt;tex &amp;gt;u[i] = (1, a[i])&amp;lt;/tex &amp;gt;. Вектора  &amp;lt;tex &amp;gt;v[j] =  (dp[j], c[j])&amp;lt;/tex&amp;gt; хотелось бы организовать так, чтобы за &amp;lt;tex &amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; находить вектор, максимизирующий выражение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt;. Посмотрим на рис. 5. Заметим интуитивно очевидный факт : красная точка (вектор) &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; не может давать более оптимальное значение &amp;lt;tex&amp;gt;v[j] \cdot u[i]&amp;lt;/tex&amp;gt; одновременно чем обе синие точки. По этой причине нам достаточно оставить выпуклую оболочку векторов &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, а ответ на запрос {{---}} это поиск &amp;lt;tex&amp;gt;v[j]&amp;lt;/tex&amp;gt;, максимизирующего проекцию на &amp;lt;tex&amp;gt;u[i]&amp;lt;/tex&amp;gt;. Это задача поиска ближайшей точки выпуклого многоугольника (составленного из точек выпуклой оболочки) к заданной прямой (из &amp;lt;tex&amp;gt;(0, 0)&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;(1, a[i])&amp;lt;/tex&amp;gt;).  Ее можно решить за &amp;lt;tex&amp;gt;O(\log(n))&amp;lt;/tex&amp;gt; двумя бинарными или одним тернарным поиском&lt;br /&gt;
Асимптотика алгоритма по-прежнему составит &amp;lt;tex&amp;gt;O(n \cdot \log(n))&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Файл:picture5convexhull.png]]&lt;br /&gt;
&lt;br /&gt;
Докажем то, что описанный выше алгоритм корректен. Для этого достаточно показать, что если имеются &amp;lt;math&amp;gt;3&amp;lt;/math&amp;gt; вектора &amp;lt;math&amp;gt;a, b, c&amp;lt;/math&amp;gt;, расположенные как на рис. 5, т.е. точка &amp;lt;math&amp;gt;b&amp;lt;/math&amp;gt; не лежит на выпуклой оболочке векторов &amp;lt;tex&amp;gt;0, a, b, c &amp;lt;/tex&amp;gt; : &amp;lt;tex&amp;gt; \Leftrightarrow [a-b, b-c] &amp;lt; 0 &amp;lt;/tex&amp;gt;, то либо  &amp;lt;tex&amp;gt;(a, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;,  либо &amp;lt;tex&amp;gt;(c, u[i])&amp;lt;/tex&amp;gt; оптимальнее, чем &amp;lt;tex&amp;gt;(b, u[i])&amp;lt;/tex&amp;gt;.&lt;br /&gt;
{{Теорема&lt;br /&gt;
|id=th12392. &lt;br /&gt;
|statement=Если есть &amp;lt;tex&amp;gt;3&amp;lt;/tex&amp;gt; вектора &amp;lt;tex&amp;gt;a, b, c&amp;lt;/tex&amp;gt;, таких что &amp;lt;tex&amp;gt;[a-b, b-c] &amp;lt; 0&amp;lt;/tex&amp;gt; то либо &amp;lt;math&amp;gt;(a, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, либо &amp;lt;math&amp;gt;(c, u) &amp;lt; (b, u)&amp;lt;/math&amp;gt;, где вектор &amp;lt;math&amp;gt;u = (1; k)&amp;lt;/math&amp;gt;.&lt;br /&gt;
|proof=По условию теоремы известно, что &amp;lt;tex&amp;gt;[a-b, b-c] &amp;lt; 0 \Leftrightarrow (a_{x} - b_{x})\cdot(b_{y} - c_{y}) &amp;lt; (a_{y} - b_{y}) \cdot (b_{x} - c_{x})&amp;lt;/tex&amp;gt; (*). Предположим (от противного), что &amp;lt;tex&amp;gt;(b, u) &amp;lt; (a, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; c_{x} + k \cdot c_{y} \Leftrightarrow (b_{x} - c_{x}) &amp;lt; k \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt; и при этом &amp;lt;tex&amp;gt;(b, u) &amp;lt; (c, u) \Leftrightarrow b_{x}  + k \cdot b_{y} &amp;lt; a_{x} + k \cdot a_{y} \Leftrightarrow (a_{x} - b_{x}) &amp;gt; k \cdot (b_{y} - a_{y})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Подставим эти неравенства в (*). Получим цепочку неравенств : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y}) = k&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - a_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{y} - c_{y})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt; &amp;lt; (a_{x} - b_{x})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (b_{y} - c_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; &amp;lt; (a_{y} - b_{y}) \cdot &amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt;(b_{x} - c_{x})&amp;lt;/tex&amp;gt; &amp;lt;tex&amp;gt;&amp;lt; k \cdot (a_{y} - b_{y})&amp;lt;/tex&amp;gt;&amp;lt;tex&amp;gt; \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Получили противоречие : &amp;lt;tex&amp;gt;k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y}) &amp;lt; k \cdot (a_{y} - b_{y}) \cdot (c_{y} - b_{y})&amp;lt;/tex&amp;gt;. Значит предположение неверно, чтд.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Из доказанной теоремы и следует корректность алгоритма.&lt;br /&gt;
&lt;br /&gt;
==См. также==&lt;br /&gt;
&lt;br /&gt;
*[[:Статические_выпуклые_оболочки:_Джарвис,_Грэхем,_Эндрю,_Чен,_QuickHull|Выпуклая оболочка]]&lt;br /&gt;
&lt;br /&gt;
*[[:Динамическое_программирование|Динамическое программирование]]&lt;br /&gt;
&lt;br /&gt;
== Примечания ==&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория: Динамическое программирование]]&lt;br /&gt;
[[Категория: Способы оптимизации методов динамического программирования]]&lt;/div&gt;</summary>
		<author><name>188.170.214.39</name></author>	</entry>

	</feed>