Алгоритм: Найти минимальную сумму k чисел из n массивов (очередей)

Предположим, что существует n очередей положительных чисел. Мне нужна минимальная сумма k чисел, выбранных из этих очередей. Обратите внимание, что это очереди, поэтому упорядочение важно, и только один номер может быть выбран из любой очереди за раз. Как только это число будет выбрано и удалено из очереди, мы можем перейти к следующей очереди в этой очереди. Поэтому сортировка недопустима (порядок важен).

Например:

Найти минимальную сумму двух чисел

2 12 3 4 
8 2 2
10 10

В приведенном выше примере я могу либо выбрать 2 из первой очереди, либо 8 из второго или 8 и 2 из второго. Оба варианта дают сумму 10.

Пример 2:

Найти минимальную сумму двух чисел

4 15 2
8 2 2
10 10

В приведенном выше примере нужно выбрать 8 и 2 как из второго списка.

Сначала я думал о линии сортированных списков слияния K, но это не сработает. Я могу думать только об одном рабочем подходе. Он должен попробовать все комбинации из всех очередей. Может ли кто-нибудь предложить лучший способ или привести меня к этому?

Ответ 1

Пусть F(qs, k) - минимальная сумма от выбора k чисел из очередей qs. Тогда:

F([], k) = 0 if k == 0, otherwise +infinity.
F([q] + qs, k) = min_i(q[0] + q[1] + ... + q[i-1] + F(qs, k-i) for i in 0...k)

То есть, если у вас нет очередей, сумма min равна 0, в противном случае вы можете взять i числа из первой очереди и k-i из остальных.

Это можно эффективно решить с помощью динамического программирования, построив таблицу (n, k), где n - количество очередей. В Python 2:

def F(qs, n, k, cache):
    if k == 0:
        return 0
    if n == 0:
        return 1e12
    if (n, k) not in cache:
        best = 1e12
        s = 0
        for i in xrange(k+1):
            if i > len(qs[len(qs)-n]):
                break
            if i > 0:
                s += qs[len(qs)-n][i-1]
            best = min(best, s + F(qs, n-1, k-i, cache))
        cache[n, k] = best
    return cache[n, k]

egs = [
    (2, [[2, 2, 3, 4], [8, 2, 2], [10, 10]]),
    (2, [[4, 15, 2], [8, 2, 2], [10, 10]]),
    (3, [[100, 100, 100], [101, 101, 2]])
]

for k, qs in egs:
    print k, qs
    print F(qs, len(qs), k, dict())
    print

Печать

2 [[2, 2, 3, 4], [8, 2, 2], [10, 10]]
4

2 [[4, 15, 2], [8, 2, 2], [10, 10]]
10

3 [[100, 100, 100], [101, 101, 2]]
204

Ответ 2

Сначала попробуйте решить более простую задачу: как найти наименьшие k элементов из длины массива m?

Инициализировать максимальный размер кучи k из первых k элементов массива (да max-heap, а не min-heap). Перемещайтесь по остальной части массива. На каждом шаге сравните текущий элемент с корнем кучи (это k-й наименьший элемент, увиденный до сих пор). Если текущий элемент меньше, удалите корень кучи и вставьте текущий элемент, соблюдая осторожность при сохранении кучи.

Когда сделано, куча содержит наименьшие k элементов из массива. Алгоритм имеет временную сложность O (m log k) и пространственную сложность O (k)


Реализация в Python. Python имеет только модуль min-heap, поэтому эмулируйте максимальную кучу, принимая отрицательное значение всего.

import heapq # min-heap operations 

def sum_smallest_k(queues, k):
    """Sum the smallest k elements across queues"""
    heap = [] # maintain a max-heap size k

    for queue in queues:
        for x in queue:
            if len(heap) < k:
                heapq.heappush(heap, -1 * x)
            else:
                heapq.heappushpop(heap, -1 * x)

    return -1 * sum(heap)

Ваши примеры

>>> sum_smallest_k([[2, 12, 3, 4], [8, 2, 2], [10, 10]], 2)
4
>>> sum_smallest_k([[4, 15, 2], [8, 2, 2], [10, 10]], 2)
4