Найти в Дзене

Задача 224. Наибольшее произведение

Последние несколько задач показывали, что Python удобнее С++, но в этой задаче всё наоборот. Давайте посмотрим.

Условие задачи с сайта acmp.ru
Условие задачи с сайта acmp.ru

Если никакие мысли по поводу решения задачи не приходят в голову, то всегда можно написать самое простое решение и позапускать его на разных входных данных. Часто после такого можно найти закономерности или способы сокращения времени работы. В этой задаче можно было бы написать три вложенных цикла и находить максимум.

Можно пойти другим путём. Что было бы, если бы все числа были натуральными? Разумно предположить, что тогда максимальное произведение дали бы три самых больших числа. Супер! Мы уже близки к решению задачи.

Что же меняется, когда появляются отрицательные числа? Лишь то, что произведение двух отрицательных чисел даёт положительное число. То есть, если взять два самых маленьких числа, то произведение может получиться больше, чем у двух самых больших. И всё? Да. А если все числа будут отрицательными? Тогда произведение трёх чисел в любом случае будет отрицательным, а это значит, что нам надо выбрать минимальное по модулю, это можно получить минимальными по модулю множителями (то есть самыми большими из имеющихся чисел).

А в чём же тогда сложность задачи? В размере входных данных. Нам даётся до миллиона чисел. То есть входной файл может быть до 8Мб (7 цифр в числе, знак минус и пробел). И это создаёт две проблемы: объём используемой памяти и время считывания такого количества данных.

Если на Python'е использовать простой список или кортеж, и даже если посылать решение на PyPy, то пройти ограничение по памяти очень сложно. На странице лучших попыток видно, что только 4 человек смогли это сделать. В то время как на С++ можно либо использовать scanf, либо известную каждому олимпиадному программисту на С++ "магическую строку":

Ускорение потокового ввода на С++
Ускорение потокового ввода на С++

После такого можно вообще не заботиться о скорости в этой задаче, и даже не обращать внимание на асимптотику решения и использовать сортировку:

Полное решение на С++
Полное решение на С++

Главное не забывайте про переполнение типов. Здесь у нас сами числа до 30000, но их произведение уже не помещается в тип int, поэтому в произведении добавлен множитель 1LL (единица типа long long), и тогда компилятор всё произведение считает в типе данных long long.

Конечно же, использование сортировки для поиска трёх максимальных чисел - это излишество, и правильнее находить их за один проход по массиву. Хммм... один проход, а это значит, что нам не надо хранить все данные в оперативной памяти.

Заведём переменные для всех необходимых нам значений:

Объявление переменных для трёх максимальных и двух минимальных значений
Объявление переменных для трёх максимальных и двух минимальных значений

И будем считывать и сразу обрабатывать входные данные:

Считывание данных
Считывание данных

Как же найти три максимальных числа? Элементарно. Мы поддерживанием ТОП-3. Если встретили число, которое больше текущего максимума, то тогда обновится весь топ. Но нельзя забывать, что число может быть меньше максимального, но превышать второй (или третий) максимум, тогда обновится соответствующая часть топа.

Поиск трёх максимальных чисел
Поиск трёх максимальных чисел

Аналогично поступаем с поиском двух минимальных чисел:

Поиск двух минимальных чисел
Поиск двух минимальных чисел

Ну, а вычисление итогового ответа остаётся почти без изменений:

Вычисление и вывод ответа
Вычисление и вывод ответа

Если отправить оба решения, то мы увидим сильное уменьшение использованной памяти, и это понятно, потому что теперь мы не храним все числа одновременно. Но вот время работы уменьшилось не очень сильно, потому что при таком размере входных данных основные его расходы именно на считывании:

Результаты работы обоих решений
Результаты работы обоих решений

Сила С++ в его скорости. А если вам удалось сдать задачу на Python, то поделитесь секретом в комментариях, всем будет интересно.

Предыдущий выпуск: Задача 131. Перепись

Я очень хочу, чтобы мои советы были полезны вам, а для того, чтобы быстрее всех получать новые статьи можно подписаться на мой канал.