Здесь уже есть много хороших ответов, включая замечательное объяснение из принятого ответа. Тем не менее, я хотел бы отметить небольшое замечание о деталях реализации на языке Python.
Первоначально я придумал решение, показанное ниже. Я ожидал получить O(N*log(N))
сложность времени, как только у нас будет один цикл for с N
итерациями, и каждая итерация выполняет двоичный поиск, который занимает максимум log(N)
.
def solution(a):
import bisect
if len(a) <= 1:
return 0
cuts = [(c - r, c + r) for c, r in enumerate(a)]
cuts.sort(key=lambda pair: pair[0])
lefts, rights = zip(*cuts)
n = len(cuts)
total = 0
for i in range(n):
r = rights[i]
pos = bisect.bisect_right(lefts[i+1:], r)
total += pos
if total > 10e6:
return -1
return total
Однако у меня O(N**2)
и сбой тайм-аута. Вы видите, что здесь не так? Правильно, эта строка:
pos = bisect.bisect_right(lefts[i+1:], r)
В этой строке вы на самом деле берете копию исходного списка, чтобы передать его в функцию бинарного поиска, и это полностью разрушает эффективность предложенного решения! Это делает ваш код немного более удобным (т. Е. Вам не нужно писать pos - i - 1
), но сильно снижает производительность. Итак, как было показано выше, решение должно быть:
def solution(a):
import bisect
if len(a) <= 1:
return 0
cuts = [(c - r, c + r) for c, r in enumerate(a)]
cuts.sort(key=lambda pair: pair[0])
lefts, rights = zip(*cuts)
n = len(cuts)
total = 0
for i in range(n):
r = rights[i]
pos = bisect.bisect_right(lefts, r)
total += (pos - i - 1)
if total > 10e6:
return -1
return total
Кажется, что иногда было бы слишком жаль делать срезы и копии, потому что Python позволяет вам делать это так легко :) Возможно, это не очень хорошая идея, но для меня это был хороший урок, чтобы уделять больше внимания этим "техническим моменты при преобразовании идей и алгоритмов в решения на основе реальных слов.