#algorithm #hashtable
#алгоритм #хэш-таблица
Вопрос:
Задано N целых чисел. Триплет чисел — это набор из трех чисел, сумма которых делится на константу M. Выясните, у скольких различных триплетов чисел из заданных N целых чисел их сумма делится на M. Какой был бы оптимальный алгоритм для нахождения ответа на эту проблему?
Пожалуйста, обратите внимание: каждый элемент рассматривается как различный, независимо от их фактического значения.
Комментарии:
1. Стоит высказать собственные соображения.
2. Правильно! Я добавил случай, который меня смущает, для которого, как я полагаю, даже фактический источник проблемы не дает правильного ответа. И я также снимаю ограничения, поскольку сам не знаю оптимального решения.
Ответ №1:
Существует O (N M ^ 2) решение.
Сначала создайте массив C [M], где C [i] содержит количество входных чисел, которые равны i mod M. Это занимает O (N) времени.
Затем для каждого i <= j <= k с i j k = 0 mod M, посчитайте тройки (a, b, c) с a = i (mod M), b = j (mod M), c = k (mod M).
Мы можем сделать это эффективно, перебрав i и j и найдя уникальное k, которое приводит к тому, что сумма равна 0 (mod M), игнорируя комбинацию, если k<j. Мы должны быть осторожны при подсчете, чтобы убедиться, что мы считаем уникальные тройки; если i, j, k различны, количество троек равно просто C [i] * C [j] * C [k], но если два или более индексов одинаковы, мы должны быть осторожны, чтобы не допустить повторного использования одного и того же числа. Например, если i = j!=k, то есть choose(C[i], 2) * C [k] троек.
Вот полный код, который выдает правильный ответ 26 для данного тестового примера:
def choose2(n):
return n * (n-1) // 2
def choose3(n):
return n * (n-1) * (n-2) // 6
def triples(A, M):
C = [0] * M
for a in A:
C[a % M] = 1
T = 0
for i in range(M):
for j in range(i, M):
k = (-i - j) % M
if k < j:
continue
if i == j == k:
T = choose3(C[i])
elif i == j:
T = choose2(C[i]) * C[k]
elif j == k:
T = C[i] * choose2(C[j])
else:
T = C[i] * C[j] * C[k]
return T
print(triples([1, 10, 4, 3, 2, 5, 0, 1, 9, 5], 5))
Комментарии:
1. вывод (triples([1,1,1,4], 3)) выдает результат 4, но очевидно, что возможны только две триплеты (1,1,1) и (1,1,4). Таким образом, правильный ответ должен быть 2, я думаю. Также спасибо, что помогли мне :))
2. Пример ссылки @Pratap Singh показывает, что 1 в
1 10 4 3 2 5 0 1 9 5
различны — выходные данные учитывают оба (0, 1, 2); и (1, 2, 7); наборы индексов. То же самое для двух пятерок (индексы 5 и 9). В этом случае есть три способа выбрать два из [1,1,1,4]3. @MBo Вы правы . О боже! Мой мозг наконец-то обрел покой. Большое вам спасибо.
Ответ №2:
Сначала я бы вычислил остаток для каждого числа после деления его на M. Затем я бы отсортировал все N чисел по их остаткам.
Это N * логарифмическая (N) стоимость.
Затем для каждой пары чисел я бы искал, есть ли соответствующий остаток среди всех чисел, который даст мне сумму, кратную M.
Существует N ^ 2 пары, и каждый поиск представляет собой двоичный журнал поиска (N).
Итак, я вижу решение стоимостью N ^ 2 * log (N), но, возможно, есть что-то более сложное и дешевое.
Комментарии:
1. Я думаю, что это решение правильное, но вам нужно не просто посмотреть, есть ли совпадающий остаток, вам нужно подсчитать, сколько там остатков (вы можете сделать это, найдя верхнюю и нижнюю части диапазона значений с правильным остатком с помощью двух двоичных поисков, чтобы избежать увеличения сложности), и вычитая 0, 1 или 2 в зависимости от того, ни один, один или оба из пары не находятся в этом диапазоне. Я думаю, проблема в том, что N может составлять до 10 ^ 5, поэтому N ^ 2, вероятно, слишком велико для того, чтобы программа работала достаточно быстро.