我有一组对象,每个对象都有一个权重和一个值。我想选择具有最高总值的一对物体,但受限于它们的总重量不超过某个阈值。另外,我得到两个数组,一个包含按权重排序的对象,另一个包含按值排序的对象。
我知道如何在O(n 2 )中做到这一点,但我怎么能在O(n)中做到?
答案 0 :(得分:3)
这是一个组合优化问题,值的排序意味着您可以轻松尝试branch and bound方法。
答案 1 :(得分:3)
答案 2 :(得分:2)
这类似于Knapsack problem。我将使用它的命名(num
- 权重,val
- 值)。
必不可少的部分:
a = 0
和b = n-1
开始。假设0
是最重对象的索引,n-1
是最轻对象的索引。a
直到对象a
和b
满足限制。b
减少一个。2.
<强>更新强>
这是背包问题,但限制为2项。您基本上需要确定第一个对象需要多少空间以及另一个对象需要多少空间。有n
种分割可用空间的重要方法,因此复杂度为O(n)
。挑选最有价值的物品以适应这些空间,无需额外费用。
答案 3 :(得分:2)
这是一个 O(n)时间,O(1)空间解决方案。
当且仅当(x不比y重)和(x不低于有价值)和(x更轻或更有价值)时,让我们调用对象x 优于对象y。如果没有对象优于x,则调用对象x 首选。存在一个最优解,包括两个第一选择对象,或第一选择对象x和对象y,使得只有x优于y。
主要工具是能够将首选对象从最轻到最重(=最有价值到最有价值),从最有价值到有价值(从最重到最轻)迭代。迭代器状态是目前按权重(resp。值)和最大值(resp.min weight)的对象索引。
以下每个步骤均为O(n)。
在扫描过程中,每当遇到不是第一选择的对象时,我们就会知道一个比它更好的对象。扫描一次并考虑这些对象。
对于从最轻到最重的每个首选对象,确定可与之配对的最重的首选对象,并考虑该对。 (所有较轻的对象都不太有价值。)由于后一个对象随着时间的推移变得更轻,所以循环的每次迭代都是摊销的O(1)。 (另请参阅搜索行和列已排序的矩阵。)
非信徒的代码。未经过严格测试。
from collections import namedtuple
from operator import attrgetter
Item = namedtuple('Item', ('weight', 'value'))
sentinel = Item(float('inf'), float('-inf'))
def firstchoicefrombyweight(byweight):
bestsofar = sentinel
for x in byweight:
if x.value > bestsofar.value:
bestsofar = x
yield (x, bestsofar)
def firstchoicefrombyvalue(byvalue):
bestsofar = sentinel
for x in byvalue:
if x.weight < bestsofar.weight:
bestsofar = x
yield x
def optimize(items, maxweight):
byweight = sorted(items, key=attrgetter('weight'))
byvalue = sorted(items, key=attrgetter('value'), reverse=True)
maxvalue = float('-inf')
try:
i = firstchoicefrombyvalue(byvalue)
y = i.next()
for x, z in firstchoicefrombyweight(byweight):
if z is not x and x.weight + z.weight <= maxweight:
maxvalue = max(maxvalue, x.value + z.value)
while x.weight + y.weight > maxweight:
y = i.next()
if y is x:
break
maxvalue = max(maxvalue, x.value + y.value)
except StopIteration:
pass
return maxvalue
items = [Item(1, 1), Item(2, 2), Item(3, 5), Item(3, 7), Item(5, 8)]
for maxweight in xrange(3, 10):
print maxweight, optimize(items, maxweight)