查找给定大小的列表的子集,该列表总和为目标值

时间:2016-01-15 08:04:26

标签: javascript algorithm combinations combinatorics

我进入了编码测试,其中一个问题是:给定一个任意长度的整数数组A,然后是两个数字N和Z,说明A中是否有Z(不同)数字,例如它们的总和是ñ

例如(格式为A N Z):

  • 对于[1,2,3] 5 2,答案是肯定的,因为2 + 3 = 5
  • 对于[1,2,3] 6 2,答案是否定的,因为A中没有两个数字可以添加到6中

我的解决方案(下面)首先列举A中每个(无序)Z编号的组合,然后对其求和,然后在总和列表中搜索N.

虽然这个解决方案工作正常(通过所有测试用例,没有超时),但我被告知分数太低,我无法继续参加测试。

所以问题是,有什么可以改进的?

一个明显的优化是立即计算每个组合的总和,然后在找到与N的匹配时停止;但由于我没有遇到时间问题,我不认为这是问题所在。什么是更好,更优雅/更有效的解决方案?

function main(a, n, z) {
    var spacea = [], // array of unordered combinations of z integers from a
        space = [], // array of unique sums of combinations from spacea
        res=0; // result (1 or 0)

    // produce combination
    spacea = combo(a,z);

    // put unique sums in space
    spacea.forEach(function(arr) {
        var s = arr.reduce(function(a,b) {
            return a+b;
            });
        if (space.indexOf(s)<0) space.push(s);
        });

    // is n in space?
    res = space.indexOf(n) === -1 ? "NO" :"YES";
    return res;
    }

// produces combinations (outputs array of arrays)                
function combo(a, z) {
    var i,
        r = [],
        head,
        right;
    if (z > a.length || z <= 0) {
        // do nothing, r is already set to []
        }
    else if (a.length === z) {
        r = [a];
        }
    else if (1 === z) {
        // r = array of array of values from a
        a.forEach(function(e) {
            r.push([e]);
            });
        }
    else { // by virtue of above tests, z>1 and z<a.length
        for (i=0; i<a.length-z+1; i++) {
            head = a.slice(i, i+1);
            right = combo(a.slice(i+1), z-1);
            right.forEach(function(e) {
                r.push(head.concat(e));
                });
            }
        }
    return r;
    }

1 个答案:

答案 0 :(得分:2)

这是subset sum problem的变体,可以使用Dynamic Programming来解决,以获得更有效的解决方案。

这里的主要区别是,您有一个额外的限制 - 必须使用的元素数量。这个额外的限制可以通过添加另一个变量(维度) - 已经使用过的元素的数量来处理。

递归公式(您将从中构建DP解决方案)应为:

D(0,0,0) = true
D(i,k,x) = false    if i < 0 or k < 0
D(i,k,x) = D(i-1, k, x) OR D(i-1, k-1, x - arr[i])

在上文中,D(i,k,x)为真,当且仅当有一个解决方案使用kk个数字时,来自第一个i元素,并且总和为x

此解决方案的复杂性为O(n*N*Z),其中n - 数组中的元素数量N - 您可以使用的不同元素的数量,Z - 目标总和。