Краткий разбор моих решений Andrey Mokhov Contest #4
Продолжаю начинание…
Внимание! В комментариях можно прочесть авторский и альтернативные подходы к решению задач.
Спасибо Андрею Мохову за очередной красивый контест
Задача A. Agreement
Если немного формализовать задачу, то она будет звучать так:
Дано логическое выражение вида (x1 | ~x1) & (x2 | ~x1) & (x2 | x3) & ,.., где xi – это глаголы в исходной задаче. Требуется определить, существует ли такое множество значений xi, при которых данное выражение истинно.
Данная задача известна как 2-SAT. Основная идея решения: построим ориентированный граф, вершинами которого будут xi и ~xi. Пусть в логическом выражении существует дизъюнкция (a | b), тогда в графе должны быть ребра ~a -> b, ~b -> a, что означает “если ~a – истина, то b тоже истина, а если ~b истина, то a – истина”. Ясно, что необходимым условием существования решения является отсутствие в этом графе цикла, в который одновременно входят c и ~c. Другими словами, c и ~c не должны одновременно находиться в одной сильно-связанной компоненте графа. Можно доказать, что это условие является и достаточным. Таким образом, решение задачи сводится к нахождению сильно-связанных компонент графа, что делается обходом в глубину за линейное время.
Задача C. Collect the coins
Попробуем смоделировать последние шаги удачной последовательности перемещений. Пусть всего монет M, а шагов K.
- после K-го шага: одна коробка с M монетами;
- после K-1-го шага: M / 2, M / 2;
- после K-2-го шага: (M / 4, M / 4, M / 2) или (3M / 4 и M / 4);
- …
Становится ясно, что количество монет в любой коробке всегда должно иметь вид pM / q, где q – степень двойки, а p – нечетное.
Вычислим для каждого ai значения pi и qi. Очевидно, что перемещать монеты нужно между такими i и j, что qi = qj. Пусть pi > pj, тогда:
pi -= pj;
pj += pj.
Понятно, что в результате данной операции pi и pj становятся четными, что позволяет сократить их как минимум на 2, не забывая про qi и qj. Повторяя такую операцию, пока возможно, получаем решение задачи. Оценка количества шагов: для каждого значения знаменателя не может быть больше N / 2 пар коробок. Количество различных значений знаменателей – log N. Количество шагов – N log N / 2, что гораздо меньше требуемых 10000.
Задача D. Disconnect the network
В задаче требуется найти минимальный разрез между всеми парами вершин. Можно доказать (если знаете – расскажите как
), что достаточно находить величины разрезов между вершиной 1 и всеми остальными. Величину разреза можно искать простым потоком: так как количество ребер небольшое, данный алгоритм работает достаточно быстро.
Задача E. Efficient sorting
Представим перестановку в виде графа с N вершинами и ребрами i -> a[i]. Можно доказать, что этот граф представляет собой набор циклов. Следовательно, если мы возьмем какое-то a[i] и поставим его на свое место, а с числом, которое стояло на месте a[i] повторим то же самое, то мы пойдем по циклу и в один прекрасный момент закончим последовательность перестановок, обойдя весь цикл. Ясно, что количество перестановок в одном цикле на единицу меньше количества вершин в этом цикле. Таким образом, отняв от количества вершин количество связанных компонент, получим ответ.
Задача F. Full-length mirror
Внимательное рассмотрение чертежа в течение нескольких секунд дает вполне очевидный ответ – половина от высоты Данила.
Задача G. Good fraction
Как всегда, когда не понятно оптимальное решение и ограничения не намного превосходят те, при которых задача решалась бы “в лоб”, попробуем эвристики. Лобовое решение:
- переберем все N от 1 до 10^D, где D – количество цифр после запятой в x;
- для каждого N вычислим соответствующее G;
- если G / N укладывается в интервал [x - 0.5 * 10 ^ (-D + 1), x + 0.5 * 10 ^ (-D + 1)), то ответ найден.
Недостаток данного решения - D может быть до 8, а 10^8 операций с 64-битными целыми это довольно долго.
Теперь оптимизации:
- очевидно, что перебирать можно не до 10^D, а до D / gcd(x, 10^D);
- если потестить значения, близкие к нулю или к единице, то можно заметить, что именно на них программа работает самое большое время. Правильное решение - таблица, например, для первых и последних 50 значений x
; - вычисление и проверку G надо делать в целых числах, используя как можно меньше операций деления.
Такие нехитрые оптимизации приводят перебор к Accepted.
Задача H. Hungry? Go eat!
Классическое название задачи - задача о двух станках. Решает ее алгоритм Джонсона. Выпишем все C[k] и E[k] в одну последовательность A и будем повторять следующие действия, пока это последовательность не опустеет:
- возьмем минимальное число из A;
- если взятое число исходно было из C, то соответствующее блюдо добавим в очередь Q;
- если взятое число исходно было из E, то соответствующее блюдо добавим в стек S.
Теперь будем заказывать все блюда из Q в соответствующем порядке, а затем из S. Эта последовательность и будет оптимальной.
| Print article | This entry was posted by arti on May 9, 2008 at 6:27 pm, and is filed under Олимпиады. Follow any responses to this post through RSS 2.0. Вы можете оставить комментарий или трэкбэк с вашего сайта. |
Русский
English
2 года назад
Спасибо за участие и разбор задач! И поздравляю с победой
Несколько комментариев от меня:
B) Задача на поиск minimum bounding ball. Решается точно за линейное ожидаемое время (например, после преобразования в задачу линейного программирования). Можно также использовать различные приближенные алгоритмы.
С) Существует и еще один способ. Он гораздо менее эффективен, и в худшем тесте использует аж 8000+ перекладываний. Имея три непустые коробки с монетами всегда можно переложить в них монеты так, одна из коробок станет пустая. Эту процедуру можно по очереди применять к трем непустым коробкам. А когда их останется только две, то любая последовательность перекладываний либо приведет к единственной коробке с монетами, либо зациклится в случае отсутствия решения.
D) Можно показать, что сложность работы алгоритма будет O(M^2), если пользоваться алгоритмом Форда-Фалкерсона для нахождения максимального потока: сложность нахождения одного увеличивающего пути равна O(M), а в сумме всех запусков алгоритма таких путей будет найдено O(M) (сумма ребер у перебираемых стоков), что дает сложность O(M^2) – довольно неочевидный факт на мой взгляд
G) Оптимальное решение задачи использует поиск подходящих дробей в дереве Фарея. Лишь один Accepted из четырех был получен таким путем. Остальные перебирали и оптимизировали разнообразнейшими способами =)
2 года назад
G. Good fraction
1. Решения укладываются в 31 бит
2. Можно перебирать не знаменатель, а числитель. Это избавит от поблем с малым x. Хватило таблицы из 10 значений.
P.S. Longlive Daenerys, похоже использовал формулу… http://www.olymp.krsu.edu.kg/ContestStatus.aspx?contest=37&author=0&upper=34907
2 года назад
> P.S. Longlive Daenerys, похоже использовал формулу…
Нет, он как раз использовал поиск в дереве Фарея.
PS Вообще его чаще называют деревом Штерна-Броко. Почитать о нем можно здесь
2 года назад
> Спасибо за участие и разбор задач! И поздравляю с победой
Пожалуйста и спасибо
2 года назад
> 1. Решения укладываются в 31 бит
В моем решении требовалось умножать 32-битные числа, так что мне надо было использовать 64 бита.
2 года назад
Spasibo za prekrasnyi razbor =)
2 года назад
Спасибо за разбор, жаль поучавствовать не удалось…