在集合之间分配数字列表以最小化集合的数量

时间:2014-11-26 22:06:10

标签: algorithm

假设我有一个循环的数字列表(最后一个元素被认为与第一个元素相邻):

10 10 10 10 9 8 10 8 19

我希望在最多给定大小的集合中分发它们,以便每个集合包含连续的数字。

对于此示例,请说我的设置大小为48。

我可以直截了当地{10 10 10 10}, {9 8 10 8}, {19}因此产生3套。

但我也可以做{10 10 10 9 8}, {10 8 19 10} - 2套(记住清单是循环的)。

如何计算可包含所有数字的最小数量?优选地,O(n)关于计算和存储。

2 个答案:

答案 0 :(得分:1)

您希望将数据划分为序列,以使每个序列的总数不超过某个常量。

选择第一个序列的起始点,例如,0。贪图地向此序列添加元素,然后添加到下一个序列,依此类推,直到消耗完所有数据。这是一个可能的分区。

要创建另一个分区,请从位置1开始。再次贪婪地使用所有数据。这是另一个可能的分区。然后从位置2开始做同样的事情,依此类推。一旦使用了属于第一个分区的第一个序列的每个起始点,就可以停止。任何进一步的起点只会产生一个与之前的分区相同的分区。

data = [10, 10, 10, 10, 9, 8, 10, 8, 19]
max_size = 48
n = len(data)

# make a partition starting from position 0
bounds = [0]
total = 0
for pos in range(n):
  x = data[pos]
  if total + x <= max_size:
    total += x
  else:
    bounds.append(pos)
    total = x

# the first partition ends here
limit = bounds[1]
best_bounds = bounds

# make all other possible partitions
for start in range(1, limit):
  bounds = [start]
  total = 0
  pos = start
  while True:
    x = data[pos]
    if total + x <= max_size:
      total += x
    else:
      bounds.append(pos)
      total = x
    pos = (pos + 1) % n
    if pos == start:
      break
  if len(bounds) < len(best_bounds):
    best_bounds = bounds

# assemble and display the best partition
partition = []
sequence = []
start = best_bounds[0]
index = 1
pos = start
while True:
  if pos == best_bounds[index]:
    partition.append(sequence)
    if pos == start:
      break
    sequence = []
    index = (index+1)%len(best_bounds)
  sequence.append(data[pos])
  pos = (pos + 1) % n
print('partition of size %d: %s' % (len(partition), partition))

答案 1 :(得分:1)

这是线性时间。

所以你有一个数组,比如[10 10 10 10 9 8 10 8 19]和一个像48这样的界限。

第一步是找出总和低于界限的所有连续元素的最大组。这样做我们将填充另一个数组,如果你有一个从这里开始,那么下一个连续组必须从这个位置开始。

要做到这一点,我们会跟踪我们从哪里开始,去哪里,以及我们当前范围的总和。每当我们的总数低于我们的范围时,我们就会推进结束并增加总数。每当我们的总数高于界限时,我们将start的下一个范围的开头标记为end,提前start,并从total中减去该元素。我们继续,直到我们发现一切都进入一个范围(在这种情况下,你的最终答案是1)或start包裹。

在这个阶段,我们总是推进一个指针,我们最多可以提前end2n次(超过我们有一个包裹和停止的时间)并结束n次(直到它包装)所以这是O(n)到目前为止。

现在我们可以从最后向后构建连接问题的答案,&#34;在我回合之前有多少组?&#34;和&#34;我在哪里与这么多团体进行交流?&#34;对于最后包装的那些位置,答案是1,无论你的next_start是什么。对于早期的答案,答案是&#34;比我的next_start&#34;和&#34;到next_start跳到. This is another n operations for O(n)`的地方。

现在我们可以扫描数组中的候选起始位置。候选起始位置是包裹自身或更远的位置。你得到的分区的大小是&#34;直到我回合的组数#34;您的答案是具有最小组数的候选起始位置。这又是O(n)步骤并为您提供答案。

因此,您的最长运行时间为O(n)