【问题标题】:Subset of values with length>=N and sum>=S长度>=N且总和>=S的值的子集
【发布时间】:2012-07-25 10:34:22
【问题描述】:

给定一个值列表(例如 10、15、20、30、70)、值 N(例如 3)和 S(例如 100),找到满足的子集:

  1. 子集长度 >= N
  2. 子集的总和 >= S

子集的总和也应该尽可能小(剩余值的总和应该尽可能大)(例如,结果子集应该是 (10,20,70),而不是 (15,20,70)满足 1. 和 2.)。

我正在研究一些问题和解决方案(背包问题、装箱问题……),但没有发现它们适用。由于某些原因(例如子集中元素的数量是固定的),互联网上的类似问题也不适合。

有人能指出我正确的方向吗?除了用尽所有可能的组合之外,还有其他解决方案吗?

编辑 - 我在 ruby​​ 代码中实现的工作算法,我想它可以进一步优化:

def find_subset_with_sum_and_length_threshold(vals, min_nr, min_sum)
    sum_map = {}
    vals.sort.each do |v|
      sum_map.keys.sort.each do |k|
        addends = sum_map[k] + [v]
        if (addends.length >= min_nr && k+v >= min_sum)
          return addends
        else
          sum_map[k+v] = addends
        end
      end
      sum_map[v] = [v] if sum_map[v].nil?
    end
  end

【问题讨论】:

    标签: algorithm dynamic-programming


    【解决方案1】:

    这和0-1背包问题没有太大区别。

    Zero-initialize a matrix with S+U rows and N columns(U is the largest list value)
    Zero-initialize a bit array A with S+U elements
    For each value (v) in the list:
      For each j<S:
        If M[N-1,j] != 0 and M[N-1, j + v] == 0:
          M[N-1, j + v] = v
          A[j + v] = true
      For i=N-2 .. 0:
        For each j<S:
          If M[i,j] != 0 and M[i+1, j + v] == 0:
            M[i+1, j + v] = v
      M[0,v] = v
    Find first nonzero element in M[N-1,S..S+U]
    Reconstruct other elements of the subset by subtracting found value from its\
      index and using the result as index in preceding column of the matrix\
      (or in the last column, depending on the corresponding bit in 'A').
    

    时间复杂度为 O(L*N*S),其中 L 是列表的长度,N 和 S 有限制。

    空间复杂度为 O(L*N)。


    Zero-initialize an integer array A with S+U elements
    i=0
    For each value (v) in the list:
      For each j<S:
        If A[j] != 0 and A[j + v] < A[j] + 1:
          A[j + v] = A[j] + 1
          V[i,j + v] = v
          P[i,j + v] = I[j]
          I[j + v] = i
      If A[v] == 0:
        A[v] = 1
        I[v] = i
      ++i
    Find first element in A[S..S+U] with value not less than N
    Reconstruct elements of the subset using matrices V and P.
    

    时间复杂度是O(L*S),其中L是列表的长度,S是给定的限制。

    空间复杂度为 O(L*S)。


    同时最小化子集大小的算法:

    Zero-initialize a boolean matrix with S+U rows and N columns\
      (U is the largest list value)
    Zero-initialize an integer array A with S+U elements
    i=0
    For each value (v) in the list:
      For each j<S:
        If A[j] != 0 and (A[j + v] == 0) || (A[j + v] > A[j] + 1)):
          A[j + v] = A[j] + 1
          V[i,N-1,j + v] = v
          P[i,N-1,j + v] = (I[j,N-1],N-1)
          I[j+v,N-1] = i
      For k=N-2 .. 0:
        For each j<S:
          If M[k,j] and not M[k+1, j + v]:
            M[k+1, j + v] = true
            V[i,k+1,j + v] = v
            P[i,k+1,j + v] = (I[j,k],k)
            I[j+v,k+1] = i
      For each j<S:
        If M[N-1, j]:
          A[j] = N-1
      M[0,v] = true
      I[v,0] = i
      ++i
    Find first nonzero element in A[N-1,S..S+U] (or the first element with smallest\
      value or any other element that suits both minimization criteria)
    Reconstruct elements of the subset using matrices V and P.
    

    时间复杂度为 O(L*N*S),其中 L 是列表的长度,N 和 S 有限制。

    空间复杂度为 O(L*N*S)。

    【讨论】:

    • 如果我是正确的,第一个解决方案实际上是在构建一个长度为 解是 (70,30,20,10)?将研究第二种解决方案。
    • 我担心第二种解决方案可能会发生冲突(例如,第一个 A[j+v] 设置为 3,然后另一个 v 可能再次将 A[j+v] 设置为另一个值,例如 2. 如果这个 A[j+v] 最后是 A[S..S+U] 中的第一个元素,我们将找不到第一个解决方案,因为它稍后在值循环中被覆盖)。但我明白您的解决方案的目标是什么 - 构建矩阵和总和数组,然后计算出哪个组合产生最接近 S 的总和。我的想法类似。
    • @MatkoMedenjak:(第一个解决方案)如果解决方案子集需要的元素多于N,则N之后的所有剩余元素都在最后一列中累积(column0->10,column1->20,column2- >30 和 70)。这是答案的初始版本中的一个错误。现在它已经修复了。
    • @MatkoMedenjak:(第二个解决方案)在 A[j+v] 设置为某个值(3)之后,它只能被更大的值(4+)覆盖。这可能会用仍然遵守所有约束的其他解决方案覆盖第一个解决方案。与第一个算法不同的是,这个算法不是第一个找到的子集,而是生成最大的子集。
    • @MatkoMedenjak:顺便说一句,OP 中存在矛盾。问题名称需要最小化子集大小,而问题主体不需要它。我的两种算法都没有最小化这个大小。但是如果你需要这个,请使用布尔矩阵的第一个算法,而不是最后一列使用整数数组(包含当前子集大小),它应该只被较小的值覆盖。要重建结果子集,请使用第二个算法中的值/父矩阵。
    【解决方案2】:

    这是Subset sum problem 的细微变化。查看Pseudo-polynomial time dynamic programming solution 部分。除了跟踪给定集合中的特定总和是否可能(即存储真/假)之外,您还需要存储长度以满足:

    1. 子集长度 >= N

    如果sum of subset = S 则与上述问题完全相同。对于sum of subset &gt;= S 条件,我想您可以在构建数组时测试此条件,如 Wiki 页面中所述。

    【讨论】:

      猜你喜欢
      • 2022-01-05
      • 2011-12-06
      • 2021-02-16
      • 1970-01-01
      • 1970-01-01
      • 2021-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多