如何在数组中找到相加等于或小于等于给定值的元素?

时间:2020-01-24 03:20:25

标签: arrays algorithm dynamic-programming subset-sum

我想找到给定正整数数组中的元素,以使它们的总和小于或等于给定值T。当它小于T时,它必须最接近T的值。我希望这是动态编程中子总和问题的变体。

例如,我有一个数组 A = [1、5、9、11、15]和T = 18答案将是元素0、1和3。 那是1 + 5 + 11 = 17

1 个答案:

答案 0 :(得分:1)

您可以在O(NS)中解决此问题,其中S以相当简单的方式是所有元素的总和。虽然子集和问题是NP完全问题,但是您可以缓存每个假的多项式时间总和(这样就不必计算重复的总和),从而可以得到伪多项式时间解。一个简单的python实现如下。这将返回可能的最小值:

def solve(arr, T):
    # the size of this set is bounded by S
    possible_sums = set()
    for i in arr:
        # incorporate sums from adding i to each already seen subset
        possible_sums |= {i + s for s in possible_sums}
        # just i is an additional possible subset
        possible_sums.add(i)
    # return the greatest <= T
    return max(s for s in possible_sums if s <= T)

请注意,如果arr中的所有元素都大于T,这将引发错误,因此,如果可能的话,您只需执行一些边缘情况检查。

实际上,返回该子集中的元素会比较棘手,但幅度不大。您只需要创建一些链接结构即可回溯。

def solve(arr, T):
    # dictionary in the form of sum: last_index_added
    possible_sums = {}
    records = []
    # do the same as before but remember the last index
    for i in range(len(arr)):
        possible_sums = {**possible_sums, **{arr[i] + s: i for s in possible_sums}}
        possible_sums[arr[i]] = i
        records.append(possible_sums)

    # find the best sum and retrace our steps on how we got here
    best_sum = max(s for s in possible_sums if s <= T)
    record_idx = len(arr) - 1
    res = []
    while best_sum:
        last_idx = records[record_idx][best_sum]
        res.append(last_idx)
        best_sum -= arr[last_idx]
        record_idx = last_idx - 1
    return res

测试用例:

>>> print(solve([1, 5, 9, 11, 15], 18))
[3, 1, 0]