Yandex测试任务

挑战赛


编写一个函数,该函数将从任意输入数组中选择总和为10的所有数字组合。

乍一看,任务很简单,您要做的就是编写一个递归并对所有值进行排序。

或者,您可以使用二进制增量,但是为了对长度为N的向量的所有值进行迭代,需要进行2 ^ n次迭代。

首先,我们选择一个特殊情况,假设该数组由数字> 0组成。之后,基于此示例,我们将实现所有情况。

步骤1


a)考虑一种特殊情况-数组的所有元素之和<10。
逻辑很简单,求和(V)<10,然后求和(V)-V [i] <求和,即,如果组合的总和始终小于10。

b)如果总和(V)= 10,则总和(V)-V [i] <总和。 结果将仅为1 v

步骤2


我们从向量中删除了所有不必要的

例如,以数组[13,10,20,13,13,18,2,9,5,8,8,9,2,1,8,16,4,15,2,2,2,1,1,5 ,5,5]。

它立即表明,如果值> 10,查找组合是没有意义的,但是所有内容将立即变得清晰。 如果我们对它进行排序:

[1、2、2、2、2、2、2、4、5、5、5、5、8、8、8、9、9、10、13、13、13、15、16、18、20 ]

在这一阶段,我们已经可以确定要删除所有大于10的元素
v => [1,1,2,2,2,2,2,4,5,5,5,5,8,8,8,9,9,10]

但实际上,所有事情都更加有趣。 我们需要将向量限制为该值,以使V [0] + V [i] <= 10; 我倾向于N; 但是如果V [i] = 10,那么我们将结果加10
v => [1,1,2,2,2,2,2,4,5,5,5,5,8,8,8,9,9]

步骤3


在已过滤的数组中有重复的元素。 如果我们将向量分解为子向量,并计算它们的和,我们将得到如下结果
[1,1] => 2
[2,2,2,2,2] => 10
[4] => 4
[5、5、5、5] => 20
[8、8、8] => 24
[9.9] => 18

我的思想的实质是,如果重复数的数量*值> 10,则考虑重复数的组合是没有意义的。也就是说,我们将向量截断,直到它们的总和小于10,然后将结果相加10。
[1,1] => [1,1]
[2,2,2,2,2] => [2,2,2,2]
[4] => [4]
[5、5、5、5] => [5]
[8,8,8] => [8]
[9.9] => [9]

作为第三步的结果,我们的向量将如下
[1、2、2、2、2、4、5、8、9]

如您所见,向量的大小已从25减少到9,它要小得多(记住迭代次数)。

负数


这有点复杂。 我们将假设我们已经实现了根据先前的限制为正排序向量查找组合的功能。
梳(arr,总和),其中sum是所需的数量,arr是从正数排序的向量

例如,取向量:
[19,15,-9,3,-13,-8,-12,11,-2,-10,0,7,17,-17,-14,-15,-11,-6,-12 ,-10,9]

1.破坏向量
A =仅正值
B =仅负值
还要看看我们是否有值= 0

2.排序
排序向量(正升序,负降序)

检查特殊情况是否为正数(步骤1)

3.验证阳性
检查正值
梳子(B,10)

3.检查是否为阴性
创建所有负数的组合,但有以下限制:

(模数元素之和)<(所有正数之和)+ 10
我们找到了新的参考和的组合(取模+ 10的元素之和)

4.平等0
如果源向量的值为0,则将所有组合+ 0添加到结果中;

由于这些限制,平均而言,当使用28和1000个数组进行长时间测试时,时间增益几乎为42%

注意不要显得紧张。 作者不为道德苦难负责。
function combine(arr, etalon = 10) { //--------------------------------------------// //------------- helpers --------------------// //    // Create binnary array function createBin(length) { let bin = []; for (let i = 0; i < length; i++) { bin[i] = false; } return bin; } //   1 //Binnary add 1 bit function binAddBit(bin, i = 0) { if (i >= bin.length) { return null; } if (bin[i] == false) { bin[i] = true; return bin; } else { bin[i] = false; i++; return binAddBit(bin, i); } } function iniq(arr) { let result = []; let object = {}; arr.forEach(vector => { value = vector.sort(function(a, b) { return a - b }); key = value.join(','); object[key] = value; }); for (var key in object) { result.push(object[key]); } return result; } //   // : //       //       //       function _combine(arr, sum = 10) { let result = []; //   if (arr[0] > sum) return []; if (arr[0] == sum) return [ [sum] ]; //1.   let $aKey = {}; let $a = []; let $sum = 0; //    $aKey[arr[0]] = arr[0]; $a.push(arr[0]); $sum += arr[0]; let $eqSum = false; for (let i = 1; i < arr.length; i++) { if ((arr[i] + arr[0]) <= sum) { //-----------------------------// // count*element < sum $aKey[arr[i]] = $aKey[arr[i]] != undefined ? $aKey[arr[i]] += arr[i] : arr[i]; if ($aKey[arr[i]] < sum) { $a.push(arr[i]); $sum += arr[i]; } //-----------------------------// //-----------------------------// // count*element = sum if ($aKey[arr[i]] === sum) { let $res = []; for (let j = 0; j < (sum / arr[i]); j++) { $res.push(arr[i]); } result.push($res); } //-----------------------------// } if (arr[i] == sum) { $eqSum = true; } } if ($eqSum) { result.push([sum]); } if ($sum < sum) return result; if ($sum == sum) { result.push($a); return result; } //   let bin = createBin($a.length); while (change = binAddBit(bin)) { let $sum = 0; let $comb = []; for (let i = 0; i < change.length; i++) { if (change[i] == false) continue; $sum += $a[i]; if ($sum > sum) break; // exit in accumulate $comb.push($a[i]); if ($sum == sum) { result.push($comb); } } } return result; } //------------- helpers --------------------// //--------------------------------------------// let result = []; //         //   -   let zero = false; //       let posetive = []; let negative = []; let sumPosetive = 0; arr.forEach(element => { if (element === 0) { zero = true; } if (element < 0) { negative.push(element); } if (element > 0) { posetive.push(element); sumPosetive += element; } }); //   ( ) posetive.sort(function(a, b) { return a - b }); negative.sort(function(a, b) { return b - a }); //       ,  //       if (sumPosetive < etalon) return []; //       if (sumPosetive == etalon) { result.push(posetive); if (zero) { let _clone = posetive.slice(); _clone.push(0); result.push(_clone); } return result; } //      ; result = result.concat(_combine(posetive, etalon)); // SUPPLE -          combinPosetiveLim negative = negative.filter(function(value) { return -value <= (sumPosetive + etalon); }); //    (  ) let bin = createBin(negative.length); //   while (changeBin = binAddBit(bin)) { let sum = etalon; let vector = []; //      for (let i = 0; i < changeBin.length; i++) { if (changeBin[i] == false) continue; sum -= negative[i]; vector.push(negative[i]); } if (sum > (sumPosetive + etalon)) continue; let lines = _combine(posetive, sum); lines.forEach(value => { result.push(vector.concat(value)); }); } //    0 if (zero) { result.forEach(item => { let _clone = item.slice(); _clone.push(0); result.push(_clone); }); } result = iniq(result); return result; } 

Source: https://habr.com/ru/post/zh-CN460923/


All Articles