Качели в игровом зале

тема: Перебор подмножеств (bitmask) · уровень: средний

Условие

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

После турнира вы выиграли n призов. У каждого приза есть «вес» (целое число). Призы нужно разложить по двум сиденьям (каждый приз — ровно на одно сиденье), чтобы разница суммарных весов была как можно меньше.

Чтобы ведущий не спорил с вами, договоримся о каноническом ответе:

Формат ввода

Первая строка: целое число n. Вторая строка: n целых чисел a1, a2, ..., an — веса призов.

Формат вывода

В первой строке выведите одно целое число — минимальную возможную разницу между суммами весов на двух сиденьях. Во второй строке выведите число k — сколько призов на левом сиденье, а затем k индексов этих призов в возрастающем порядке (канонический выбор по правилам выше).

Ограничения

Пример

Ввод:

4
10 7 6 5

Вывод:

2
2 2 3

В этом примере на левое сиденье кладём призы с весами 7 и 6 (индексы 2 и 3), на правое — 10 и 5. Разница равна 2, и это минимум.

Как решать — идея подхода

Приём: Перебор подмножеств битмаской

Ключевое наблюдение: раскладка однозначно задаётся выбором множества призов для левого сиденья. Тогда правая сумма — это total - left, а разница равна abs(total - 2*left). По правилу каноничности нам нужны только варианты с left <= right, то есть left <= total//2 — это сразу режет половину перебора.

Почему работает перебор: n <= 20, значит подмножеств 2^n всего до 1 048 576 — это реально посчитать в лоб.

План:

Сложность: O(n * 2^n) по времени и O(1) доп. памяти (кроме хранения ответа).

Частая ошибка: сравнивать лексикографически «маски» или строки, а не списки индексов. Правильнее сравнивать именно последовательности индексов слева (например, [2,3] < [2,4], а [2,3] < [2,3,5]).

Разберись руками

Есть 4 приза с весами 10, 7, 6, 5. Каждый приз кладём либо на левое сиденье, либо на правое. Нужно выбрать, что кладём налево, чтобы разница весов получилась как можно меньше, и при этом слева было не тяжелее, чем справа.

Идея: Сначала считаем общий вес. Потом перебираем все варианты, какие призы положить слева, и оставляем только те, где слева не тяжелее. Для каждого такого варианта считаем разницу между сиденьями и выбираем вариант с самой маленькой разницей; если лучших несколько — берём тот, у которого список индексов слева «самый ранний» при сравнении по порядку.

Решить задачу с автопроверкой на Python →

Куда дальше