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

Я знаю, что это можно сделать, сортируя массив и принимая большие числа, пока не будет выполнено требуемое условие. Это займет по крайней мере NLog(n) время сортировки.

есть ли какие-либо улучшения по сравнению с nlog(n).

мы можем предположить, что все числа положительны.

5 ответов


вот алгоритм, который O(n + size(smallest subset) * log(n)). Если наименьшее подмножество намного меньше массива, это будет O(n).

читать http://en.wikipedia.org/wiki/Heap_%28data_structure%29 если мое описание алгоритма неясно (это свет на деталях, но детали все есть).

  1. превратите массив в кучу, расположенную так, чтобы самый большой элемент был доступен во времени O(n).
  2. повторно извлеките самый большой элемент из кучи, пока их сумма не станет достаточно большой. Это занимает O(size(smallest subset) * log(n)).

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

Edit: вот еще один вариант, который часто быстрее, но может быть медленнее.

Walk through elements, until the sum of the first few exceeds S.  Store current_sum.
Copy those elements into an array.
Heapify that array such that the minimum is easy to find, remember the minimum.
For each remaining element in the main array:
    if min(in our heap) < element:
        insert element into heap
        increase current_sum by element
        while S + min(in our heap) < current_sum:
            current_sum -= min(in our heap)
            remove min from heap

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


предполагая, что числа являются целыми числами, вы можете улучшить обычный n lg(n) сложность сортировки, потому что в этом случае у нас есть дополнительная информация о том, что значения находятся между 0 и S (для наших целей целые числа больше S совпадают с S).

поскольку диапазон значений конечен, вы можете использовать несопоставимый алгоритм сортировки, такой как Сортировка По Ячейкам или Radix Сортировать ниже n lg(n).

обратите внимание, что эти методы зависит от некоторой функции S, поэтому, если S становится достаточно большим (и n остается достаточно маленьким), вам может быть лучше вернуться к сравнительному виду.


одно улучшение (асимптотически) над тэта(nlogn), которое вы можете сделать, - это получить алгоритм времени O(n log K), где K-требуемое минимальное количество элементов.

таким образом, если K является постоянным или, скажем, log n, это лучше (асимптотически), чем сортировка. Конечно, если K - N^epsilon, то это не лучше, чем тета(N logn).

способ сделать это-использовать алгоритмы выбора, который может сказать вам, что яth самый большой элемент в O(n) время.

теперь выполните двоичный поиск K, начиная с i=1 (самый большой) и удваивая i и т. д. На каждом повороте.

вы найдете ith самый большой, и найти сумму I самых больших элементов и проверить, если он больше, чем S или нет.

таким образом, вы будете запускать o(log K) запусков алгоритма выбора (который является O(n)) для общего времени выполнения O(N log K).


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

альтернативно, это действительно просто quickselect С небольшим дополнительным ведением бухгалтерского учета на оставшуюся сумму.

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

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

вот psuedo код, я не тестировал его для крайних случаев,но это дает представление.

def Solve(arr, s):
  # We could get rid of worse case O(n^2) behavior that basically never happens 
  # by selecting the median here deterministically, but in practice, the constant
  # factor on the algorithm will be much worse.
  p = random_element(arr)
  left_arr, right_arr = partition(arr, p)
  # assume p is in neither left_arr nor right_arr
  right_sum = sum(right_arr)
  if right_sum + p >= s:
    if right_sum < s:
      # solved it, p forms the cut off
      return len(right_arr) + 1    
    # took too much, at least we eliminated left_arr and p
    return Solve(right_arr, s) 
  else:
    # didn't take enough yet, include all elements from and eliminate right_arr and p
    return len(right_arr) + 1 + Solve(left_arr, s - right_sum - p)  

  1. исключить числа
  2. pigeon-hole сортировка чисел

элементы сумме убывания в порядке, пока вы превысите С.