有效的组合算法传递时间限制

时间:2014-06-26 08:24:12

标签: c++ algorithm

我正在做一个练习编程问题,并且坚持通过这个问题的时间限制。是否有其他有效的算法来通过时间限制?

我已经对数组进行了排序,但我认为这仍然没有多大帮助。

这是我的伪代码:

sort boxA
sort boxB
for i in boxA:
      for j in boxB:
          if i+j < value:
              break
          else if i+j > value:
              count+=1
print the count
set count = 0

问题是要求输出大于或等于该值的boxA + boxB的组合数。

输入:

5 3 1200                #number of boxA | number of boxB | value
100 110 160 750 1030    #number of boxA
400 500 500             #number of boxB

输出:

5

说明: 将boxA和boxB组合在一起的方法有五种,因此值> gt = = value

1. 750 + 500
2. 750 + 500
3. 1030 + 400
4. 1030 + 500
5. 1030 + 500

BoxA和boxB的列表中可以有500000个项目。我认为这种测试用例可以限制我的算法。

您能否展示另一种有效算法来传递此问题的时间限制?谢谢。

4 个答案:

答案 0 :(得分:1)

对于A中具有a项的每个方框,B中具有项数加a,大于或等于某个值d的方框数将等于方框数其项目数大于(d - a)。

首先,排序数组B,然后对于A中值为x的每个框,使用二进制搜索从B中的哪个索引开始查找,框中的项目更大或等于d - x。添加最终结果(n - index),其中n是B中的项目数。

时间复杂度为O(m log n)

示例:

我们有两个数组A是{1,5,9,2,4,5},B是{1,3,3,4,5,6,7,8};

我们希望找到两个总和大于7的盒子。

因此,对于A

中的每个元素

1 - &gt;我们使用二分搜索来找到B中大于或等于(7 - 1)的最小元素的索引,现在在索引5处,所以我们添加到结果(8 - 5)(其中8是元素的数量)在B)。

5 - &gt;我们需要在B - >中找到(7 - 5)。我们在搜索后有索引1 - &gt;将(8 - 2)添加到结果中。

...

答案 1 :(得分:0)

您可以使用以下内容:Live example

std::size_t Count(std::vector<int>& A, std::vector<int>& B, int N)
{
    std::vector<int>& a = A.size() < B.size() ? A : B;
    std::vector<int>& b = A.size() < B.size() ? B : A;

    std::sort(b.begin(), b.end());
    std::size_t res = 0;
    for (int e : a)
    {
        auto it = std::lower_bound(b.begin(), b.end(), N - e);
        res += std::distance(it, b.end());
    }
    return res;
}

答案 2 :(得分:0)

你需要分而治之的策略。假设范围[b1,e1)和[b2,e2]已排序,以下过程可以完成工作。

#include <iterator>

template<class It1, class It2, class T>
size_t do_work(It1 b1, It1 e1, It2 b2, It2 e2, T const t){
    if (b1 == e1 || b2 == e2) return 0;

    auto const n1 = std::distance(b1, e1);
    auto const n2 = std::distance(b2, e2);
    if (n1 > n2) return do_work(b2, e2, b1, e1, t);// always divide the shorter sequence

    auto const l11 = n1 / 2, l12 = n1 - l11;
    auto const l21 = n2 / 2, l22 = n2 - l21;
    auto const m1 = std::next(b1, l11);
    auto const m2 = std::next(b2, l21);

    if (*m1 + *m2 > t){
        return do_work(b1, m1, b2, e2, t) + do_work(m1, e1, b2, m2, t) + l12 * l22;
    }
    else{
        auto const _m1 = std::next(m1);
        return do_work(b1, _m1, std::next(m2), e2, t) + do_work(_m1, e1, b2, e2, t);
    }
}

如果It1和It2是随机访问迭代器,则时间复杂度约为O(n log2(n)),其中n = max(n1,n2)。

答案 3 :(得分:0)

我假设您可以对两个数组进行排序,并且可以向前或向后遍历每个数组。

sort boxA
sort boxB
let a = first number in boxA    // the smallest number in the set
let b = last number in boxB     // the largest
let total = 0
let subtotal = 0

while a exists
{
    while (b exists) and (a + b >= value)
    {
        let b = previous number in boxB   // "Move" the boxB iterator one place
                                          // toward the start of the array.
        let subtotal = subtotal + 1   // Now subtotal is the number of times
                                      // we have "moved" the boxB iterator since
                                      // the algorithm started to execute.
    }

    // Now subtotal is the number of numbers b in boxB such that a + b >= value.

    total += subtotal
    let a = next number in boxA
}

print total

如果boxAboxB包含重复的条目(多次使用相同的数字值),则“下一个数字”表示“数字的下一个副本”,而不是“下一个唯一数字”,以及与“之前的号码”类似。

我本可以将while a exists ... let a = next number in boxA改为for a in boxA,但我想强调此算法处理boxA的方式与其对应方式之间的关系对待boxB:它通过boxA(向前方向)和一次通过boxB(向后方向)同时进行一次迭代。

特别是,与典型的嵌套循环控制结构不同,对于来自boxB的每个新值,我们 not 将迭代器设置为boxA“返回到开始”。 相反,在算法的整个执行过程中,“内循环”只能迭代boxB中的数字的次数。 因此,算法的运行时间是对两个阵列进行排序所花费的时间 加上一个额外的O(n),其中n是较大数组的大小。 当然,如果算法接收未排序的数组,最坏情况下的成本是O(n log n),因为排序,但它仍然比需要额外的O(n log n)步骤的算法更快(通过常数因子)在对数组进行排序之后。 如果我们假设数组已经排序(由于某些其他原因),那么算法只在O(n)时间内运行。

<强>更新

正如评论中所指出的那样,如果您知道首先在boxA中有多少项(在O(1)时间内很容易确定某些数据结构 - 特别是“数组”中的数据)常用语言),当boxA中的所有数字都被访问时,您可以添加突破boxB循环的逻辑。只需这样做,而不是迭代额外的remaining_size_of_Atotal += remaining_size_of_A * subtotal。 (请注意,此时subtotal等于boxB的大小。) 这可以节省几个步骤。