L-R поток

Правка ru2, от Noobish_Monk, 2025-07-08 14:50:12

Недавно я встретил задачу 2046D - For the Emperor!, которая напомнила мне про существование L-R потоков. Я их узнал очень давно, да и в тот момент задачи помимо "просто напиши L-R поток" нам не дали, так что я не запомнил алгоритм и мне пришлось узнать его заново, уже более детально. К сожалению блога на кфе, который бы всё понятно объяснил, не было, так что пришлось лазить по всяким другим ресурсам. Я нашёл их тут, примерно понял идею, потом пришлось смотреть реализацию, у меня к ней остались вопросы, я воспринимал немного как чёрный ящик. Но сегодня в 3 часа ночи мне пришло озарение и я всё понял. Чтобы тем 5 людям, которые прочитают мой блог, не пришлось его ждать, я хочу тут разъяснить моё понимание этого алгоритма и как его удобно писать. (Prerequisites: задача о максимальном потоке, любой алгоритм для её решения)

Что такое вообще L-R поток?

В обычной версии максимального потока нам даются исток $$$S$$$, сток $$$T$$$, а также рёбра $$$(u, v, c)$$$, означающие, что из вершины $$$u$$$ в вершину $$$v$$$ есть направленное ребро с пропускной способностью $$$c$$$. По ребру может течь поток $$$0 \le f \le c$$$. В такой сети требуется найти максимальный поток. То есть нам дано верхнее ограничение на поток. В версии $$$L-R$$$ на каждое ребро накладывается также и ограничение снизу, то есть теперь даются рёбра $$$(u, v, l, r)$$$~--- теперь по ребру может течь поток $$$l \le f \le r$$$. Изначально может показаться, что это какое-то странное ограничение, но в некоторых задачах оно возникает вполне естественно. Давайте же научимся решать такую версию задачи о максимальном потоке.

Для этого сведём её к обычной задаче о поиска потока. Давайте скажем, что по всем рёбрам мы сразу пустим $$$l$$$ потока. У нас, очевидно, скорее всего сломается сеть~--- в некоторые вершины будет приходить больше потока, чем уходить, а некоторые, наоборот, будут требовать поток. В некотором роде это ведь обычная задача на потоки~--- тоже есть две доли, одна поток выдаёт, вторая принимает, хотим все насытить. Чтобы к этому свести, сделаем такую модификацию сети. Введём ещё один исток $$$S'$$$ и сток $$$T'$$$, а каждое ребро $$$(u, v, l, r)$$$ заменим на привычные рёбра $$$(u, v, r - l)$$$, $$$(S', v, l)$$$ и $$$(u, T', l)$$$. Идея за этим следующая~--- в вершину $$$v$$$ пришло $$$l$$$ потока, который надо отдать, из вершины $$$r$$$ ушло $$$l$$$ потока, который надо вернуть.

Но какой смысл в смене истока и стока. Как поток будет идти из "перенасыщенных" вершин в "недонасыщенные"? Сейчас я сделаю последнее изменение сети~--- $$$(T, S, \inf)$$$~--- и объясню, зачем оно нужно. Рассмотрим какое-нибудь ребро $$$(u, v, l, r)$$$. Я заменил его на $$$(u, v, r - l)$$$, $$$(S', v, l)$$$, $$$(u, T', l)$$$, то есть я уже пустил $$$l$$$ потока по ребру, а также добавил требование его как-то вернуть. Но обычно мы ведь пускаем поток из $$$S$$$ в $$$T$$$, то есть это ребро должно быть на пути из $$$S$$$ в $$$T$$$. Попробуем пустить поток из $$$S'$$$ в $$$T'$$$. Пусть мы нашли путь $$$S', v, v_1, \ldots, v_k, T, S, v_{k+1}, \ldots, v_m, u, T'$$$ и пустили по нему $$$l$$$ потока. Тогда мы по сути дополнили путь из $$$S$$$ в $$$T$$$, проходящий через ребро $$$(u, v)$$$.

История

 
 
 
 
Правки
 
 
  Rev. Язык Кто Когда Δ Комментарий
en14 Английский Noobish_Monk 2025-07-08 18:11:00 9
en13 Английский Noobish_Monk 2025-07-08 18:02:02 0 (published)
ru16 Русский Noobish_Monk 2025-07-08 16:10:11 0 (опубликовано)
en12 Английский Noobish_Monk 2025-07-08 16:08:07 2 Tiny change: 'on of the ,ax flow pr' -> 'on of the max flow pr'
ru15 Русский Noobish_Monk 2025-07-08 16:07:18 22
en11 Английский Noobish_Monk 2025-07-08 16:05:44 22
ru14 Русский Noobish_Monk 2025-07-08 16:04:57 2
ru13 Русский Noobish_Monk 2025-07-08 16:04:40 4
ru12 Русский Noobish_Monk 2025-07-08 16:04:14 96
en10 Английский Noobish_Monk 2025-07-08 16:03:50 10 Initial revision for English translation
en9 Английский Noobish_Monk 2025-07-08 16:03:07 47 Initial revision for English translation
en8 Английский Noobish_Monk 2025-07-08 16:02:13 0 Initial revision for English translation
en7 Английский Noobish_Monk 2025-07-08 16:02:02 2 Initial revision for English translation
en6 Английский Noobish_Monk 2025-07-08 16:01:42 15 Initial revision for English translation
en5 Английский Noobish_Monk 2025-07-08 16:00:57 5 Initial revision for English translation
en4 Английский Noobish_Monk 2025-07-08 16:00:21 8 Initial revision for English translation
en3 Английский Noobish_Monk 2025-07-08 16:00:00 3 Initial revision for English translation
en2 Английский Noobish_Monk 2025-07-08 15:59:43 4 Initial revision for English translation
en1 Английский Noobish_Monk 2025-07-08 15:59:26 9319 Initial revision for English translation (saved to drafts)
ru11 Русский Noobish_Monk 2025-07-08 15:44:19 7 Мелкая правка: ' cout << '\n';\' -> ' cout << ans << '\n';\'
ru10 Русский Noobish_Monk 2025-07-08 15:43:20 80
ru9 Русский Noobish_Monk 2025-07-08 15:42:52 1710
ru8 Русский Noobish_Monk 2025-07-08 15:28:13 3233
ru7 Русский Noobish_Monk 2025-07-08 15:26:13 793 Мелкая правка: ' $(u, v)$.' -> ' $(u, v)$.\n\n![ ](https://codeforces.me/6bd2e0/flow1 (1)-1.png)'
ru6 Русский Noobish_Monk 2025-07-08 14:59:01 75 Мелкая правка: ' $(u, v)$.' -> ' $(u, v)$.\n\n![ ](https://codeforces.me/6bd2e0/flow1 (1)-1.png)'
ru5 Русский Noobish_Monk 2025-07-08 14:55:37 19 Мелкая правка: ', v)$.\n\n' -> ', v)$.\n\n![ ](flow1 (1))'
ru4 Русский Noobish_Monk 2025-07-08 14:54:56 15 Мелкая правка: ', v)$.\n\n' -> ', v)$.\n\n![ ](flow1 (1))'
ru3 Русский Noobish_Monk 2025-07-08 14:53:07 4 Мелкая правка: ' $(u, v)$.' -> ' $(u, v)$.\n\n'
ru2 Русский Noobish_Monk 2025-07-08 14:50:12 2258
ru1 Русский Noobish_Monk 2025-07-08 14:24:41 858 Первая редакция (сохранено в черновиках)