我正在做一个练习编程问题,并且坚持通过这个问题的时间限制。是否有其他有效的算法来通过时间限制?
我已经对数组进行了排序,但我认为这仍然没有多大帮助。
这是我的伪代码:
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个项目。我认为这种测试用例可以限制我的算法。
您能否展示另一种有效算法来传递此问题的时间限制?谢谢。
答案 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
如果boxA
或boxB
包含重复的条目(多次使用相同的数字值),则“下一个数字”表示“数字的下一个副本”,而不是“下一个唯一数字”,以及与“之前的号码”类似。
我本可以将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_A
次
total += remaining_size_of_A * subtotal
。
(请注意,此时subtotal
等于boxB
的大小。)
这可以节省几个步骤。