<?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=Mariel21</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=Mariel21"/>
		<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/Mariel21"/>
		<updated>2026-05-18T01:51:17Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=Meet-in-the-middle&amp;diff=42014</id>
		<title>Meet-in-the-middle</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=Meet-in-the-middle&amp;diff=42014"/>
				<updated>2014-12-09T18:06:14Z</updated>
		
		<summary type="html">&lt;p&gt;Mariel21: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Определение&lt;br /&gt;
|definition=&lt;br /&gt;
'''Meet-in-the-middle''' (Встреча в середине)  — это метод решения уравнения вида &amp;lt;tex&amp;gt; f({x}) = g({y}) &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; x \in {X} &amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt; y \in {Y} &amp;lt;/tex&amp;gt;, который работает за время &amp;lt;tex&amp;gt; O(F(X) + Y \cdot G_X(y))&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt; F(X) &amp;lt;/tex&amp;gt; {{---}} время построения множества &amp;lt;tex&amp;gt; X &amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt; G_X(y) &amp;lt;/tex&amp;gt; {{---}} время поиска элемента &amp;lt;tex&amp;gt; x &amp;lt;/tex&amp;gt; в множестве &amp;lt;tex&amp;gt; X &amp;lt;/tex&amp;gt;, удовлетворяющее решению при заданном &amp;lt;tex&amp;gt; y &amp;lt;/tex&amp;gt;, или проверка, что такого &amp;lt;tex&amp;gt; x &amp;lt;/tex&amp;gt; не существует.&lt;br /&gt;
}}&lt;br /&gt;
'''Meet-in-the-middle''' разбивает задачу пополам и решает всю задачу через частичный расчет половинок. Он работает следующим образом: переберем все возможные значения &amp;lt;tex&amp;gt; {x} &amp;lt;/tex&amp;gt; и запишем пару значений &amp;lt;tex&amp;gt; ({x},{f({x})}) &amp;lt;/tex&amp;gt;  в множество. Затем будем перебирать всевозможные значения &amp;lt;tex&amp;gt; y &amp;lt;/tex&amp;gt;, для каждого из них будем вычислять &amp;lt;tex&amp;gt; g(y) &amp;lt;/tex&amp;gt;, которое мы будем искать в нашем множестве. Если в качестве множества использовать отсортированный массив, а в качестве функции поиска {{---}} [[Целочисленный двоичный поиск | бинарный поиск]], то время работы нашего алгоритма составляет &amp;lt;tex&amp;gt; {O(X\log{X})} &amp;lt;/tex&amp;gt; на сортировку, и &amp;lt;tex&amp;gt; {O(Y\log{X})} &amp;lt;/tex&amp;gt; на двоичный поиск, что дает в сумме &amp;lt;tex&amp;gt;{O((X + Y)\log{X}})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Задача о нахождении четырех чисел с суммой равной нулю ==&lt;br /&gt;
Дан массив целых чисел &amp;lt;tex&amp;gt;{A}&amp;lt;/tex&amp;gt;. Требуется найти любые &amp;lt;tex&amp;gt; 4 &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} = ({2,3,1,0,-4,-1}) &amp;lt;/tex&amp;gt;. Решением данной задачи является, например, четверка чисел &amp;lt;tex&amp;gt; 3 + 1 + 0 - 4 = 0&amp;lt;/tex&amp;gt; или &amp;lt;tex&amp;gt; 0 + 0 + 0 + 0 = 0&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Наивный алгоритм заключается в переборе всевозможных комбинаций чисел. Это решение работает за &amp;lt;tex&amp;gt; {O(N^4)}&amp;lt;/tex&amp;gt;. Теперь, с помощью '''Meet-in-the-middle''' мы можем сократить время работы до &amp;lt;tex&amp;gt; {O(N^2\log{N}}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Для этого заметим, что сумму &amp;lt;tex&amp;gt; a + b + c + d = 0 &amp;lt;/tex&amp;gt; можно записать как &amp;lt;tex&amp;gt; a + b = -(c + d)&amp;lt;/tex&amp;gt;. Мы будем хранить все &amp;lt;tex&amp;gt; {N^2} &amp;lt;/tex&amp;gt; пар сумм &amp;lt;tex&amp;gt; a + b &amp;lt;/tex&amp;gt; в массиве &amp;lt;tex&amp;gt; sum &amp;lt;/tex&amp;gt;, который мы отсортируем. Далее перебираем все &amp;lt;tex&amp;gt; {N^2} &amp;lt;/tex&amp;gt; пар сумм &amp;lt;tex&amp;gt; c + d &amp;lt;/tex&amp;gt; и проверяем [[Целочисленный двоичный поиск|бинарным поиском]], есть ли сумма &amp;lt;tex&amp;gt; -(c + d) &amp;lt;/tex&amp;gt; в массиве &amp;lt;tex&amp;gt; sum &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
  // sum - массив сумм a + b, cnt - счетчик массива sum&lt;br /&gt;
  '''findsum'''():&lt;br /&gt;
    '''for''' a = 0..N - 1&lt;br /&gt;
      '''for''' b = 0..N - 1&lt;br /&gt;
        sum[cnt].res = A[a] + B[b]&lt;br /&gt;
        sum[cnt].a = a&lt;br /&gt;
        sum[cnt].b = b&lt;br /&gt;
        cnt++&lt;br /&gt;
    sort(sum, key = &amp;quot;res&amp;quot;) // сортируем sum по полю res&lt;br /&gt;
    '''for''' c = 0..N - 1&lt;br /&gt;
      '''for''' d = 0..N - 1&lt;br /&gt;
        '''if''' сумма -(A[c] + A[d]) есть в массив sum&lt;br /&gt;
           index = индекс суммы -(A[c] + A[d])&lt;br /&gt;
           '''return''' (sum[index].a, sum[index].b, A[c], A[d])&lt;br /&gt;
    '''return''' &amp;quot;No solution&amp;quot;&lt;br /&gt;
Итоговое время работы &amp;lt;tex&amp;gt; {O(N^2\log{N}}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&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; {w_{i} \leqslant 10^{9}} &amp;lt;/tex&amp;gt; ) и ценностью (&amp;lt;tex&amp;gt;{cost_{i} \leqslant 10^{9}} &amp;lt;/tex&amp;gt;). В рюкзак, ограниченный по весу, необходимо набрать вещей с максимальной суммарной стоимостью. Для ее решения изначальное множество вещей N разбивается на два равных(или примерно равных) подмножества, для которых за приемлемое время можно перебрать все варианты и подсчитать суммарный вес и стоимость, а затем для каждого из них найти группу вещей из первого подмножества с максимальной стоимостью, укладывающуюся в ограничение по весу рюкзака. Сложность алгоритма &amp;lt;tex&amp;gt;O({2^{N/2}}\cdot{N})&amp;lt;/tex&amp;gt;. Память &amp;lt;tex&amp;gt; O({2^{N/2}})&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Реализация ===&lt;br /&gt;
Разделим наше множество на две части. Подсчитаем все подмножества из первой части и будем хранить их в массиве &amp;lt;tex&amp;gt; first &amp;lt;/tex&amp;gt;. Отсортируем массив &amp;lt;tex&amp;gt; first &amp;lt;/tex&amp;gt; по весу. Далее пройдемся по этому массиву и оставим только те подмножества, для которых не существует другого подмножества с меньшим весом и большей стоимостью. Очевидно, что подмножества, для которых существует другое, более легкое и одновременно более ценное подмножество, можно удалять.&lt;br /&gt;
Таким образом в массиве &amp;lt;tex&amp;gt; first &amp;lt;/tex&amp;gt; мы имеем подмножества, отсортированные не только по весу, но и по стоимости. Тогда начнем перебирать все возможные комбинации вещей из второй половины и находить бинарным поиском удовлетворяющие нам подмножества из первой половине, хранящиеся в массиве &amp;lt;tex&amp;gt; first &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
Реализуем данный алгоритм:&lt;br /&gt;
  // N - количество всех вещей, w[] - массив весов всех вещей, cost[] - массив стоимостей всех вещей, R - ограничение по весу рюкзака.&lt;br /&gt;
  '''knapsack'''():&lt;br /&gt;
    sn = N / 2&lt;br /&gt;
    fn = N - sn&lt;br /&gt;
    '''for''' mask = 0..2 ** sn  - 1&lt;br /&gt;
      '''for''' j = 0..sn&lt;br /&gt;
        '''if''' j-ый бит mask == 1&lt;br /&gt;
          first[i].w += w[j];&lt;br /&gt;
          first[i].c += cost[j]&lt;br /&gt;
    сортируем first по весу&lt;br /&gt;
    '''for''' i = 0..2 ** sn - 1&lt;br /&gt;
      '''if''' существует такое подмножество с индексом j, что first[j].w &amp;lt;tex&amp;gt; \leqslant &amp;lt;/tex&amp;gt; first[i].w '''and''' first[j].c &amp;lt;tex&amp;gt; \geqslant &amp;lt;/tex&amp;gt; first[i].c&lt;br /&gt;
        удалим множество с индексом i из массива first&lt;br /&gt;
  &lt;br /&gt;
    '''for''' mask = 0..2 ** fn - 1&lt;br /&gt;
      '''for''' j = 0..fn&lt;br /&gt;
        '''if''' j-ый бит mask == 1&lt;br /&gt;
          curw += w[j + sn]&lt;br /&gt;
          curcost += cost[j + sn]&lt;br /&gt;
    &lt;br /&gt;
      index = позиция, найденная бинарным поиском в массиве first, подмножества с максимальным весом, не превыщающим R - curv&lt;br /&gt;
      '''if''' first[index].w &amp;lt;tex&amp;gt; \leqslant &amp;lt;/tex&amp;gt; R - curw '''and''' first[index].c + curcost &amp;lt;tex&amp;gt; &amp;gt; &amp;lt;/tex&amp;gt; ans&lt;br /&gt;
        ans = first[index].c + curcost&lt;br /&gt;
    '''return''' ans&lt;br /&gt;
&lt;br /&gt;
Итоговое время работы &amp;lt;tex&amp;gt; {O({2^{N/2}}\cdot({N}+\log{2^{N/2}}))} = O({2^{N/2}}\cdot{N}) &amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Задача о нахождении кратчайшего расстояния между двумя вершинами в графе ==&lt;br /&gt;
[[Файл:bfs.png|600px|thumb|right|Нахождение кратчайшего расстояния между двумя вершинами]]&lt;br /&gt;
Еще одна задача, решаемая '''Meet-in-the-middle'''  —  это нахождение кратчайшего расстояния между двумя вершинами, зная начальное состояние, конечное состояние и то, что длина оптимального пути не превышает &amp;lt;tex&amp;gt; N &amp;lt;/tex&amp;gt;.&lt;br /&gt;
Стандартным подходом для решения данной задачи, является применение алгоритма [[Обход в ширину|обхода в ширину]]. Пусть из каждого состояния у нас есть &amp;lt;tex&amp;gt; K &amp;lt;/tex&amp;gt; переходов, тогда бы мы сгенерировали &amp;lt;tex&amp;gt; {K^{N}} &amp;lt;/tex&amp;gt; состояний. Асимптотика данного решения составила бы &amp;lt;tex&amp;gt; {O({K^{N}})} &amp;lt;/tex&amp;gt;. '''Meet-in-the-middle''' помогает снизить асимптотику до &amp;lt;tex&amp;gt; {O({K^{N/2}})} &amp;lt;/tex&amp;gt;. &amp;lt;br&amp;gt;&lt;br /&gt;
=== Алгоритм решения === &lt;br /&gt;
&lt;br /&gt;
1. Сгенерируем '''bfs'''-ом все состояния, доступные из начала и конца за &amp;lt;tex&amp;gt; {N/2} &amp;lt;/tex&amp;gt; или меньше ходов.&lt;br /&gt;
&lt;br /&gt;
2. Найдем состояния, которые достижимы из начала и из конца.&lt;br /&gt;
&lt;br /&gt;
3. Найдем среди них наилучшее по сумме длин путей. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Таким образом, '''bfs-ом''' из двух концов, мы сгенерируем максимум &amp;lt;tex&amp;gt; {O({K^{N/2}})} &amp;lt;/tex&amp;gt; состояний.&lt;br /&gt;
&lt;br /&gt;
== См. также ==&lt;br /&gt;
* [[Обход в ширину]]&lt;br /&gt;
* [[Целочисленный двоичный поиск]]&lt;br /&gt;
&lt;br /&gt;
==Cсылки==&lt;br /&gt;
*[http://infoarena.ro/blog/meet-in-the-middle Meet-in-the-middle]&lt;br /&gt;
*[http://g6prog.narod.ru/dpl.ps Лекции по информатике (36 страница)]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Динамическое программирование ]]&lt;/div&gt;</summary>
		<author><name>Mariel21</name></author>	</entry>

	</feed>