假设有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排序列表的行,但它没有工作。我只能想到一种有效的方法。它是尝试所有队列中的所有组合。 有人可以建议更好的方式或指导我吗?
答案 0 :(得分:13)
让F(qs, k)
成为从队列qs
中选择k个数字的最小总和。然后:
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)
也就是说,如果您没有剩余队列,则最小总和为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
答案 1 :(得分:0)
首先尝试解决一个更简单的问题:如何从数组长度m?
中找到最小的k个元素从数组的前k个元素初始化最大堆大小k(是max-heap而不是min-heap)。循环遍历阵列的其余部分。在每个步骤中,将当前元素与堆的根进行比较(这是到目前为止看到的第k个最小元素)。如果当前元素较小,则删除堆根并插入当前元素,小心保持堆不变。
完成后,堆包含数组中最小的k个元素。该算法具有时间复杂度O(m log k)和空间复杂度O(k)
在Python中实现。 Python只有min-heap module,因此通过消除所有内容的负面来模拟最大堆。
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