[算法]一个大小为N(<30000)的数组中,找A+B=C使得C最大

Paradin 2008-05-21 10:11:24
如题。要求最好几秒内出结果
请不要使用枚举那样会要很长时间谢谢。
...全文
3766 148 打赏 收藏 转发到动态 举报
写回复
用AI写文章
148 条回复
切换为时间正序
请发表友善的回复…
发表回复
JackBurd 2012-03-12
  • 打赏
  • 举报
回复
这样的题才有意思,不太难也不是很简单。现在我正在根据各位好的想法写程序,又学了一点。
yaoyaozii 2010-04-28
  • 打赏
  • 举报
回复
比较水的做法应该是
排序O(nlogn)
按降序确定C
然后B
查找C-B存在否O(1)

最坏是找不到的情况O(n^2)

?
myknight 2010-03-21
  • 打赏
  • 举报
回复
窃以为137#的这种方法是错的, 虽然暂时还没有想出一个反例, 而且如果我没有理解错的话测试用的是随机奇数测试, 也就是已知答案是NO的情况. 其实这个算法的问题就在于可能"漏解", 如果你测试一个没有解的情况自然永远都是对的, 关键是在解很少且很难取到的情况下能不能找到解. 当然我也希望我这个想法是错的....因为我暂时还没有一个特别好的(也就是O(NlgN)以下)的方法, 不过一个O(N^2)的基于奇偶分析的方法还是比较成熟了, 等我自己先测试确定一下再贴出来吧~~~

[Quote=引用 137 楼 lingol 的回复:]

30楼好样的,只是a、b的增减无需一个一个地来,用二分查找快很多。
我实现了这个算法,1千万的输入2.1秒左右,1亿的25秒左右
——当然最坏情况会加倍,但是基本上复杂度降到了nlogn,即使是最坏情况。
std::lower_bound()的作用是找到第一个不小于某值的数。

C/C++ code

bool foo(int* m, unsigned long len, int&a……
[/Quote]
mugumugu 2008-10-24
  • 打赏
  • 举报
回复
鉴于该问题未规定元素的取值区间,则其区间为(-∞,+∞),
楼上的很多人都进入了一个误区,而其实其结果存在三种情形:
1. a + b = c 且 c 同时 < a,b
2. a + b = c 且 c 同时 > a,b
3. a + b = c 且 c 存在于 a,b之间

所以说,所有未区分以上情形,而去预先规定c取某值,并且只在c值的一侧寻找a,b的方法
在正确性上是值得商榷的!!!!!
呵呵~~~~

也即,用以上方法,只是使用了约束2,
对于a+b=c有以下结论:
如果2中未找到,则并不代表解不存在,解可能存在于空间1,3;
但是2中如果找到了,则『如果正解不存在于空间3,则该解便是正解』;
woshinishi 2008-10-24
  • 打赏
  • 举报
回复
鉴于该问题未规定元素的取值区间,则其区间为(-∞,+∞),
楼上的很多人都进入了一个误区,而其实其结果存在三种情形:
1. a + b = c 且 c 同时 < a,b
2. a + b = c 且 c 同时 > a,b
3. a + b = c 且 c 存在于 a,b之间

所以说,所有未区分以上情形,而去预先规定c取某值,并且只在c值的一侧寻找a,b的方法
在正确性上是值得商榷的!!!!!
呵呵~~~~
freeCodeSunny 2008-06-08
  • 打赏
  • 举报
回复
排序啊!C就最先确定了,B依次取值,A=C-B,用二分查找,能找到则返回TRUE,否则继续向下执行.
Vitin 2008-05-31
  • 打赏
  • 举报
回复
算法的时间复杂度是包含所有操作的(只要这项操作花费了时间),加法也好,比较也好,都要包含。
(空间复杂度也类似,包含所有花费空间的数据结构)

不过一般而言,时间复杂度的计算精度是数量级,此时,在多个时间等价的操作之间,可以仅仅计算最常用的操作(比如当比较操作多于加法操作时,由于一次比较所花费的时间和一次加法是同等数量级的,可以只计算比较操作的复杂度)。
Vitin 2008-05-31
  • 打赏
  • 举报
回复
呃,抱歉,140楼对于137楼的评价是错误的,一开始的判定并非必要,因为lower_bound可以保证这一点,它只是稍稍降低了效率。
主要问题在于每次for循环使用了前一次的b,从而忽略了许多的查找。
andy_cai 2008-05-31
  • 打赏
  • 举报
回复
看了一些回答,我只是在想,算法的复杂度是那么计算的么?
这个问题是加法(或者减法)和数值比较
这两者的算法复杂度不一样的,我们需要关心的是加法的复杂度

既然只有不到30000个数,那是否可以从静态表查询角度来减少加法运算?
Vitin 2008-05-31
  • 打赏
  • 举报
回复
lingol 的算法也是一条思路,不过137楼有一些瑕疵:
首先,只有在当前 a + b < c 时才可以查找 a,在a + b > c 时才可以查找 b,未判定前直接查找是不对的。
其次,考虑到重复值的问题,在查找 b 时,以 upper_bound 获得的结果再减一为佳。

之前我也考虑过用二分法对30楼算法做优化,可是效果不佳,时间大约为线性查找的2到3倍。
仔细想想这是正常的。例如在 a + b < c 时,从 a 开始线性查找第一个a' 使得 a' + b >= c,一般而言确实比二分法要快,因为 a' 并非在 a 到 b 之间随机分布,而是相当接近 a 的。

以下是我的代码(注释掉的代码为线形查找和hash查找):

void func()
{
const size_t MAX_SIZE = 30000;
int numbers[MAX_SIZE];
hash_set<int> hs;

srand(static_cast<unsigned int>(time(0)));
for (int i = 0; i < MAX_SIZE; ++i)
{
numbers[i] = rand() | 1;
// hs.insert(numbers[i]);
}

sort(numbers,numbers+MAX_SIZE);

for (int* i = numbers+MAX_SIZE-1; i >= numbers; --i)
{
//for (int * j = i-1; j >= numbers; --j)
// if (hs.find(*i-*j) != hs.end())
// {
// cout << "FOUND! : A = " << *j << "\tB = " << *i-*j << "\tC = " << *i << endl;
// return;
// }

int* j = numbers, *k = i-1;
while(j < k)
{
if (*j + *k == *i)
{
cout << "FOUND! : A = " << *j << "\tB = " << *k << "\tC = " << *i << endl;
return;
}
else if (*j + *k > *i)
// k--;
{
int* p = upper_bound(j+1, k, *i-*j); // upper_bound return first p such that *p > *i-*j
k = p-1;
}
else if (*j + *k < *i)
// j++;
{
int* p = lower_bound(j+1, k, *i-*k); // lower_bound return first p such that *p >= *i-*k
j = p;
}
}
}

cout << "UNFOUND!" << endl;
}
lingol 2008-05-30
  • 打赏
  • 举报
回复
这是随机奇数测试
lingol 2008-05-30
  • 打赏
  • 举报
回复
刚才测试了一下,VS2008的Release版,优化全开:
1亿的输入:62.56s
1千万的输入:5.157s
1百万的输入:0.42s
30万的输入:0.12s
lingol 2008-05-30
  • 打赏
  • 举报
回复
30楼好样的,只是a、b的增减无需一个一个地来,用二分查找快很多。
我实现了这个算法,1千万的输入2.1秒左右,1亿的25秒左右
——当然最坏情况会加倍,但是基本上复杂度降到了nlogn,即使是最坏情况。
std::lower_bound()的作用是找到第一个不小于某值的数。


bool foo(int* m, unsigned long len, int& a, int& b, int& c)
{
std::sort(m, m + len);
b = m[len - 2];
int *l, *r, *ptr;
for (unsigned long i = len - 1; i > 1; i--)
{
c = m[i];
l = m, r = m + i - 1;
while ( l < r)
{
// find for *ptr >= c - b
ptr = std::lower_bound(l, r, c - b);
a = *ptr;
if (a == c - b) return (true);
l = ptr;

// find for *ptr >= c - a
ptr = std::lower_bound(l, r, c - a);
b = *ptr;
if (b == c - a) return(true);
r = ptr - 1; // move one step at least
}
}
return (false);
}
fish_autumn 2008-05-30
  • 打赏
  • 举报
回复
如果30000个素数包含2,3,5的话还是能找到的
langzhengyi 2008-05-30
  • 打赏
  • 举报
回复
这个题出得不对,我要是给你30000个素数,那是肯定找不到的呀!!
languagec 2008-05-29
  • 打赏
  • 举报
回复
复杂度为N*N.

思路:
1. 升序排列数组
2. 取最大值为C
3. 取B为次大值,A为最小值
4. 判断A+B,如果大于C,B向前取一个更小值;如果小于C,A向后取一个更大值;如果等于C,则直接返回。循环此操作,直到找到或A >= B.
5. 经过步骤4,如果没取到合适的值,C向前取一更小值,重复4.

分析:
排序后,针对固定的C,判断有无合适的A与B时,最坏情况下只需要进行一个简单的遍历即可得出判断,所以最坏情况下,复杂度为N*N.

证明略。
呵呵,这道题是我们算法课的作业。是较简单的贪心算法的应用。不难!



应该就是这么做了。
Edidu 2008-05-29
  • 打赏
  • 举报
回复
强烈支持63楼,基本打破N*N。
应该是以上100多楼中最好的算法。

[Quote=引用 63 楼 thinkallday 的回复:]
复杂度为N*N.

思路:
1. 升序排列数组
2. 取最大值为C
3. 取B为次大值,A为最小值
4. 判断A+B,如果大于C,B向前取一个更小值;如果小于C,A向后取一个更大值;如果等于C,则直接返回。循环此操作,直到找到或A >= B.
5. 经过步骤4,如果没取到合适的值,C向前取一更小值,重复4.

分析:
排序后,针对固定的C,判断有无合适的A与B时,最坏情况下只需要进行一个简单的遍历即可得出判断…
[/Quote]
Edidu 2008-05-29
  • 打赏
  • 举报
回复
嗯,这样也不错。
如果以C/2为分界点,类似
1 ---> A ---> C/2 ---> B ---> C
这样的区间划分。
然后,如果A+B > C
A往小方向前进
如果A+B < C
B往大的方向前进
直到A+B = C 或者A,B出界

这个算法也不错
但是我认为他与63楼算法的时间复杂度是一样的
我还是更倾向于63楼
代码简单些

Do until b>a
if D(a)+D(b) > D(1) then
b = b + 1
elseif D(a)+D(b) < D(1) then
a = a + 1
else
exit loop
end if
loop

[Quote=引用 131 楼 panda_lin 的回复:]
楼上的,63楼的算法思路和我一样,但肯定是N*N的复杂度.
可以改进的是:
取最大C,然后以C/2为分界点找AB,后面步骤一样。

排序复杂度最坏为N*N

找A+B=C的复杂度为N,循环N轮,最后复杂度为N*N。
[/Quote]
gongyali2005 2008-05-29
  • 打赏
  • 举报
回复
java写的.交率不是太高.

public class Text
{
public void display(int [] a){
java.util.Arrays.sort(a);
c: for(int i=a.length-1;i>=0;i--){
for(int j=0;j<i-1;j++){
if(a[i]==a[j]+a[j+1]){
System.out.println(a[i]+"="+a[j]+"+"+a[j+1]);
break c;
}}
}
}
public static void main(String [] args){
int a[]=new int[3000];
for(int i=0;i<3000;i++){
a[i]=(int)(Math.random()*3000);
}
new Text().display(a);
}
}

记事本写的.格式没弄.
熊主任 2008-05-29
  • 打赏
  • 举报
回复
楼上的,63楼的算法思路和我一样,但肯定是N*N的复杂度.
可以改进的是:
取最大C,然后以C/2为分界点找AB,后面步骤一样。

排序复杂度最坏为N*N

找A+B=C的复杂度为N,循环N轮,最后复杂度为N*N。
加载更多回复(128)

64,637

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧