Разбор задачи демо-варианта 2022 года
Немного исправим условие задачи, сделаем ее более универсальной.
Задача
Дана последовательность из N
натуральных чисел. Рассматриваются все её непрерывные подпоследовательности, такие что сумма элементов каждой из них кратна K
. Найдите среди них подпоследовательность с максимальной суммой, определите её длину. Если таких подпоследовательностей найдено несколько, в ответе укажите количество элементов самой короткой из них.
Прежде чем разобраться в алгоритме решения данной задачи, необходимо вспомнить некоторые моменты из теории чисел. А именно, деление с остатком.
Любое число (
a
) представимо в виде:
a = c * k + r
, где
c
- результат целочисленного деления числа
a
на
k
,
r
- остаток от деления
a
на
k
. Если число
a
делится на
k
, то
r = 0
.
Допустим, что сумма (
s1
) некторых чисел не кратна
k
. Запишем ее в виде
s1 = c1 * k + r1
. Какое число из данной суммы надо убрать, чтобы сумма оставшихся чисел делилась на
k
?
Представим число, которое будем убирать в виде
a = c * k + r.
Запишем разницу:
s2 = s1 - a = c1 * k + r1 - (c * k + r
) = k * (c1 - c) + (r1 - r).
s2 = k * (c1 - c) + (r1 - r) - результат данного выражения будет делиться на
k
в том случае, если
r1-r=0
. Следовательно,
r1=r
. Другими словами, сумма
s1
и число
a
должны иметь один и тот же остаток при делении на
k
.
Значит, в задаче нам необходимо найти некоторую подпоследовательность чисел, начинающуюся с первого элемента последовательно, у которой есть собственный префикс, сумма элементов которого при делении на k дает тот же остаток, что и сумма всех элементов подпоследовательности. При этом сумма элементов префикса должна быть минимальной, а сумма элементов подпоследовательности максимальной. Тогда их разница даст нам искомую максимальную сумму.
Сумма элементов будет минимальной, если в ней меньше всего элементов (т.к. в последовательности только натуральные числа), максимальной - если в ней больше всего элементов.
Алгоритм
1) При каждом новом считанном числе, увеличиваем сумму всех элементов последовательности (
s
). Также необходимо посчитать, какой остаток от деления на
k
будет иметь данная сумма:
r = s % k
.
Для того, чтобы сумма
s
делилась на
k
необходимо из нее убрать сумму элементов собственного префикса, которая имеет тот же остаток при делении на
k
, что и
s
.
2) Если такого префикса нет, значит при считывании числа, мы получили данный префикс, и нужную последовательность определить не можем. Сохраним сумму данного префикса в массиве (
prefix[r]
), его длину в другом массиве (
mn_size
). Длина префикса равна номеру считанного числа (при счете с 1):
mn_size[r] = i + 1
.
3) Если же такой префикс есть в массиве, то можем вычислить сумму искомой последовательности, как
s - prefix[s % k]
. Длина такой последовательности будет равна
i - mn_size[n % k]
.
4) При вычислении искомой суммы, ее необходимо проверять на максимальное значение и, в случае равенства с максимальным значением, проверять длину, чтобы в результате она оказалась минимальной.
Попробуйте реализовать данный алгоритм самостоятельно.