【问题标题】:Finding the number of sum combinations between two arrays that satisfy a condition查找满足条件的两个数组之间的总和组合数
【发布时间】:2021-05-27 07:11:25
【问题描述】:

问题:

我有 2 个数组 A[v] 和 M[w],长度为 分别为 vw。给定两个数字 pq,我想找出这些数组的两个元素之和有多少组合满足以下条件:

p >= A[v] + M[w] q

一个例子:

让:

A = [9, 14, 5, 8, 12, 2, 16],
v = 7,
M = [6, 2, 9, 3, 10],
w = 5,
p = 21,
q = 24

由于以下组合,答案将是 5:

  1. 14 + 9 = 23
  2. 14 + 10 = 24
  3. 12 + 9 = 21
  4. 12 + 10 = 22
  5. 16 + 6 = 22

我尝试过的:

以下是该问题在C++中的实现:


int K = 0; // K is the answer
for (int i=0; i<v; i++) {
    for (int j=0; j<w; j++) {
        if (A[v]+M[w] >= p && A[v]+M[w] <= q) {
            ++K;
        }
    }
}

如我们所见,上面的代码在循环中使用了循环,从而使程序的时间复杂度 Ο(v×w) 对于大型程序来说相当慢数组。

问题

有没有最快的方法解决这个问题?

【问题讨论】:

  • 尚未彻底测试,但排序和二进制搜索可能适用。
  • constraints 是什么? maximum possible size of the array 是什么? range of p and q 是什么? possible size of each element of the array 是什么?

标签: c++ arrays algorithm math combinations


【解决方案1】:

问题总结:给定两个数组AB,大小分别为vw,求A中的一个元素和B中的一个元素的可能配对数,使得两个元素的总和为 &gt;= p&lt;= q

简单的蛮力算法本质上就是您目前所拥有的。蛮力算法将简单地涉及测试所有可能的对,正如您所说,这将具有O(v*w) 的时间复杂度,因为有v 选择第一个元素的方法和w 选择第二个元素的方法测试所有对。

正如@thestruggler 在他们的评论中指出的那样,可以应用排序和二分搜索来创建更高效​​的算法。

假设我们按升序对B 进行排序。对于您提供的测试用例,我们将:

A = [9, 14, 5, 8, 12, 2, 16]

B = [2, 3, 6, 9, 10]

p = 21 and q = 24

现在,请注意,对于 a 中的每个元素,我们可以计算 B 中元素的 范围,当添加到元素时,总和将介于 p 和 @987654337 之间@。我们实际上可以通过使用所谓的Binary SearchO(logW) 时间找到这个范围。具体来说,如果我们希望将A 中的第一个数字与B 中的数字配对,我们将二进制搜索第一个元素的索引&gt;= 12,然后二进制搜索最后一个元素的索引是&lt;= 15B 中与 A 中的元素配对的元素数量正好等于 1 加上两个索引之间的差。

总的来说,这个算法的复杂度为O(WlogW + VlogW)(或O(VlogV + WlogV);如果你想超越你的程序,可以决定对更大的数组进行排序以节省测试时间)。这是因为对具有N 元素的数组进行排序需要O(NlogN) 时间,并且因为对具有N 元素的排序数组的每次二分搜索都需要O(logN)

【讨论】:

  • 复杂度为O((V+W)log(VW))
【解决方案2】:

这也可以通过以下方式解决,

首先对两个数组进行排序,
[9, 14, 5, 8, 12, 2, 16] => [2, 5, 8, 9, 12, 14, 16]
[6, 2, 9, 3, 10] => [2, 3, 6, 9, 10]

现在迭代较小数组的所有元素并执行以下操作,

[2, 3, 6, 9, 10],
当前元素是2,用p 减去它,可以说它是num 这意味着,
num = p - 2 = 21 - 2 = 19
然后其他数组中的所有数字,大于等于19 将与21 相加。但是其他数组中没有元素大于或等于19 这意味着将2与其他数组的任何元素相加不能大于或等于p,

下一个元素是3,它也不能满足要求,其他元素也是如此,所以我们直接移到元素9进行解释,

[2, 3, 6, 9, 10]
num = p - 9 = 21 - 9 = 12 并通过获得12 的下限,我们将获得所有数字,与9 的总和将大于或等于至p(21),如下所示,
[2, 5, 8, 9, 12, 14, 16],
这些数字与9 的总和大于或等于p,现在是时候找出它们中有多少会产生小于或等于q 的总和,所以我们必须这样做以下,
num = q - 9 = 24 - 9 = 15 并通过找到15 的上限将给出所有与9 的数字总和应小于等于q,如下所示,
[2, 5, 8, 9, 12, 14, 16],

通过这种方式,您可以找到所有具有 sum 的组合,p &gt;= sum &lt;= q

#include <iostream>

#include <vector>
#include <algorithm>

std::size_t combinationCount(int p, int q, std::vector<int> arr1, std::vector<int> arr2){

    std::sort(arr1.begin(), arr1.end());
    std::sort(arr2.begin(), arr2.end());

    std::vector<int>::const_iterator it1 = arr1.cbegin();
    std::vector<int>::const_iterator endIt1 = arr1.cend();

    std::vector<int>::const_iterator it2 = arr2.cbegin();
    std::vector<int>::const_iterator endIt2 = arr2.cend();

    if(arr2.size() < arr1.size()){
        std::swap(it1, it2);
        std::swap(endIt1, endIt2);
    }

    std::size_t count = 0;

    for(; endIt1 != it1; ++it1){

        int num = p - *it1;

        std::vector<int>::const_iterator lowBoundOfPIt = std::lower_bound(it2, endIt2, num);

        if(endIt2 != lowBoundOfPIt){

            num = q - *it1;
            std::vector<int>::const_iterator upBoundOfQIt = std::upper_bound(it2, endIt2, num);

            count += (upBoundOfQIt - lowBoundOfPIt);
        }
    }

    return count;
}

int main(){

    std::cout<< "count = "<< combinationCount(21, 24, {9, 14, 5, 8, 12, 2, 16}, {6, 2, 9, 3, 10})<< '\n';
}

输出:5

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-15
    • 2019-10-07
    • 2020-05-11
    • 1970-01-01
    • 1970-01-01
    • 2012-01-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多