存在ai * aj = ak

来源:互联网 发布:休闲零食市场数据 编辑:程序博客网 时间:2024/06/11 18:42
给定一个正整数集合 s = {a1, a2, ..., an},
存在ai * aj = ak, i != j != k
试找出满足上述条件的最大数ak,如果不存在满足上述条件的三个数,则输出-1
回复人:xwzheng(蚩尤) ( 一级(初级)) 信誉:100 2007-7-27 16:25:39 得分:0     ?

o(n3)肯定能解出

Top   回复人:tailzhou(尾巴) ( 一星(中级)) 信誉:100 2007-7-27 17:19:47 得分:0     ?

排序再穷举,
可以到O(N^2lgN);

肯定有复杂度比较低的解法,不然google不会拿出来的;
Top   回复人:tengomi() ( 一级(初级)) 信誉:100 2007-7-27 18:54:35 得分:0     ?

排序再做,是可以到O(N^2lgN)
不知道还有没有复杂度更低的方法
Top   回复人:uzone(没事干,就来csdn多交流,看别人的源代码。) ( 二级(初级)) 信誉:100 2007-7-27 20:17:10 得分:0     ?

应该可以用数论的知识解决,实验室关门了,明天看。
Top   回复人:galois_godel() ( 一级(初级)) 信誉:100 2007-7-27 20:21:15 得分:0     ?

O(N^2)

先从小到大排序

for(i=0;i<n;i++)
{
for(j=0,k=i;j<=i && k<n;)
{
if(a[i]*a[j]==a[k])return; //find
else if(a[i]*a[j]>k)k++;
else j++;
}
}

return -1;
Top   回复人:galois_godel() ( 一级(初级)) 信誉:100 2007-7-27 20:23:39 得分:0     ?

修正下,前面没看到i != j != k
for(i=0;i<n;i++)
{
for(j=0,k=i+1;j<i && k<n;)
{
if(a[i]*a[j]==a[k]){
return; //find
}
else if(a[i]*a[j]>k)k++;
else j++;
}
}

return -1;
Top   回复人:galois_godel() ( 一级(初级)) 信誉:100 2007-7-27 20:24:41 得分:0     ?

还有点小错,
for(i=0;i<n;i++)
{
for(j=0,k=i+1;j<i && k<n;)
{
if(a[i]*a[j]==a[k]){
return; //find
}
else if(a[i]*a[j]>a[k])k++;
else j++;
}
}

return -1;

Top   回复人:sylow_abel(庄生晓梦) ( 一级(初级)) 信誉:100 2007-7-27 20:53:02 得分:0     ?

int max=-1;
for(i=0;i<n;i++)
{
for(j=0,k=i+1;j<i && k<n;)
{
if(a[i]*a[j]==a[k]){
if(a[k]>max)max=a[k];
}
else if(a[i]*a[j]>a[k])k++;
else j++;
}
}

return max;
Top   回复人:galois_godel() ( 一级(初级)) 信誉:100 2007-7-27 20:55:40 得分:0     ?

一转眼功夫,居然转移阵地了
Top   回复人:medie2005(阿诺) ( 二级(初级)) 信誉:100 2007-7-27 20:57:55 得分:0     ?

先按递增排序,复杂度o(nlogn)

计算在已排好序的数组中的首元a[0]的平方,即a[0]*a[0]。用二分查找,找到在已排好序的数组中不小于a[0]*a[0]的最低位置big_pos,若big_pos==len,说明无解。

否则:
对区间[big_pos,len)的元素(len是数组的长度),从尾开始向前扫描。

对元素ak,计算它的取整平方根sak=int(sqrt(ak)),用二分查找,找到在已排好序的数组中大于sak的最低位置pos。

对已排好序的数组中区间[0,pos)内的元素ai,检查ak/ai是否为正整数,
a): 若不是,++i;
b): 若是,用二分查找确定区间[pos,k)是否存在值为ak/ai的数,若存在,结束;否则,++i。



Top   回复人:tengomi() ( 一级(初级)) 信誉:100 2007-7-27 23:27:01 得分:0     ?

根据medie2005(阿诺)的提示,写了以下程序,测了几组数据,还没有发现问题,正继续测试中
/**
* 正整数集合s = {a1, a2, ..., an}
* 存在 ai * aj = ak, i != j != k
* 求满足条件的最大整数k,如果不存在,则输出-1
*/
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;


int array[] = { 1, 2, 3, 6, 4, 7, 19, 20, 25 , 80};

/**
* 二分查找,找到集合中不大于x的最高位置或不低于x的最低位置
*/
template<class Type>
int BinarySearch(Type arr[], const Type& x, int n)
{
int left = 0;
int right = n - 1;
while(left <= right) {
int middle = (left + right) / 2;
if(x == arr[middle])
return middle;
if(x > arr[middle])
left = middle + 1;
else
right = middle - 1;
}
return left - 1;
}

/**
* 严格的二分查找
*/
template<class Type>
int BinarySearchAccurate(Type arr[], const Type& x, int low, int high)
{
int left = low;
int right = high - 1;
while(left <= right) {
int middle = (left + right) / 2;
if(x == arr[middle])
return middle;
if(x > arr[middle])
left = middle + 1;
else
right = middle - 1;
}
return -1;
}

//打印数组arr
template<class Type>
void PrintArray(Type arr[], int n)
{
int i;
for(i = 0; i < n; ++i)
{
cout<<arr[i]<<" ";
}
cout<<endl;
}

//计算n的平方
int ComputeSquare(int n)
{
return n * n;
}

//判断double是否为一个整数
bool IsInteger(double n)
{
if(n == (int)n)
return true;
return false;
}

//求解,array为待分析的数组,n为数组长度
//如果存在,则返回ak
//否则返回-1
int Compute(int array[], int n)
{
//首先计算a[0]的平方
int temp = ComputeSquare(array[0]);
int big_pos = BinarySearch<int>(array,temp,n);
if(temp == n - 1)
{
return -1; //无解
}
else
{
//对于(big_pos, len)
for(int i = n - 1; i >= big_pos; --i)
{
temp = int(sqrt((double)array[i]));
int low_pos = BinarySearch<int>(array,temp,n);
for(int j = 0; j <= low_pos; )
{
double temp2 = (double)array[i] /(double) array[j];

if(!IsInteger(temp2))
++j;
else
{
int k = BinarySearchAccurate<int>(array, (int)temp2, low_pos + 1, i );
if(k != -1)
return array[i];
else
++j;
}
}
}
}
return -1; //无解
}

int main()
{
sort(array, array + sizeof(array)/sizeof(array[0]));
// qsort(array, sizeof(array), sizeof(array) / sizeof(array[0]));
PrintArray<int>(array, sizeof(array) / sizeof(array[0]));
// cout<<BinarySearch<int>(array,13,sizeof(array) / sizeof(array[0]))<<endl;
cout<<"Result: "<<Compute(array, sizeof(array) / sizeof(array[0]))<<endl;
return 0;
}


Top   回复人:tengomi() ( 一级(初级)) 信誉:100 2007-7-27 23:29:18 得分:0     ?

喔,上面程序开头的注释写错了
求满足条件的最大整数k,如果不存在,则输出-1
应该是
求满足条件的最大整数ak,如果不存在,则输出-1
Top   回复人:tailzhou(尾巴) ( 一星(中级)) 信誉:100 2007-7-28 12:26:57 得分:0     ?

medie2005(阿诺)的方法的复杂度能证明是O(NlogN)的么?

我觉得最坏的情况下的复杂度是O(N^2);
考虑{2,4,16,256,....,2^(2*(N-1)))的数组;

计算在已排好序的数组中的首元a[0]的平方,即a[0]*a[0]。用二分查找,找到在已排好序的数组中不小于a[0]*a[0]的最低位置big_pos,若big_pos==len,说明无解。
// big_pos=1;

否则:
对区间[big_pos,len)的元素(len是数组的长度),从尾开始向前扫描。

对元素ak,计算它的取整平方根sak=int(sqrt(ak)),用二分查找,找到在已排好序的数组中大于sak的最低位置pos。
//pos=k-1;

对已排好序的数组中区间[0,pos)内的元素ai,检查ak/ai是否为正整数,
a): 若不是,++i;
b): 若是,用二分查找确定区间[pos,k)是否存在值为ak/ai的数,若存在,结束;否则,++i。
//区间[0,pos)内的元素ai,ak/ai都是整数,且在[pos,k)不存在值为ak/ai的数;
//所以必须搜索所有的[0,pos)范围内所有的数;

总的代价应该是O(N^2);



Top   回复人:tailzhou(尾巴) ( 一星(中级)) 信誉:100 2007-7-28 12:37:38 得分:0     ?

galois_godel() 贴的代码好象也有点问题;
只是找到有ak,但不是要求的最大的ak;

应该这样的:

for(i=n-1;i>=2;i--)
{
for(j=0,k=i-1;k>j;)
{
int s=a[j]*a[k];
if (s==a[i]) return; a[i];
else if (s>a[i]) k--;
else j++;
}
}
return -1;

Top   回复人:loops(迷茫) ( 二级(初级)) 信誉:100 2007-7-28 13:23:33 得分:0     ?

假设有数组A[1...n]
(1)首先从小到大排序;
(2)假设ai<=aj<=ak
从末尾最大的元素开始,最左边ai从A[1]开始向右搜索,最右边aj从A[i]/A[1]向左搜索
找到的第一个满足条件的ak就是该题的解

算法伪码描述如下:
n = Length[A];
for i=n to 1 step -1
leftBound = 1;
rightBound = A[i]/A[1]; //确定搜索的右边界
while ( leftBound<rightBound )
temp = A[leftBound]*A[rightBound];
if ( temp < A[i] )
then leftBound++;
else if( temp == A[i] )
then return A[i]; //返回,找到该最大的值
else
rightBound -- ;
return -1; //没找到

在最坏的情况下,时间复杂度是O(nlogn+ n2 )
Top   回复人:loops(迷茫) ( 二级(初级)) 信誉:100 2007-7-28 13:30:13 得分:0     ?

上面写错了,应该算法是:
n = Length[A];
for i=n to 1 step -1
//确定搜索的左边界
leftBound = 1;

//确定搜索的右边界
k = A[i]/A[1];
rightBound = i;
while( A[rightBound] > k )
rightBound --;

while ( leftBound<rightBound )
temp = A[leftBound]*A[rightBound];
if ( temp < A[i] )
then leftBound++;
else if( temp == A[i] )
then return A[i]; //返回,找到该最大的值
else
rightBound -- ;
return -1; //没找到

最坏的情况是:1,2,2,7,9,............ 最大的ak是a3=2
最好的情况是:1, ..........., 199, 199 时间复杂度是O(1)
Top   回复人:lin_style(﹏.贾诩(我的昵称太淫荡,你看不着~)) ( 三级(初级)) 信誉:100 2007-7-28 13:40:58 得分:0     ?

loops(迷茫)算新颖。。。
Top   回复人:division(好风长吟) ( 一级(初级)) 信誉:100 2007-7-28 17:53:56 得分:0     ?

1. 不排序,先利用找最大算法找出第一大的值Max
2. 然后利用Max的平方根r作为划分标准将集合s划分为两部分A、B。使得A中小于r,B中大于r
3. 令|A|=a, |B|=b
4. if a>b
5. then 按照从小到大顺序排序B中元素
6. 对A中每个元素x利用二分检索检查在B中是否有元素y使得x*y=s
7. else 按照从小到大顺序排序A中元素
8. 对B中每个元素y利用二分检索检查在A中是否有元素x得x*y=s
9. 不满足的话跳回步骤1找第二大值赋给Max直到所有数都选择后输出-1

屈婉玲老师在我们的课上好像就是说的这个题目

Top   回复人:loops(迷茫) ( 二级(初级)) 信誉:100 2007-7-28 18:55:05 得分:0     ?

看来在最坏情况下,O(n^2)的时间复杂度逃不掉,都是同一个量级的,各个算法的区别只是在于具体计算步骤多几步或少几步。
loops的算法跟tailzhou的算法实质差不多,只是没有进行二分查找和用sqrt()去限定查找结束的范围。
Top   回复人:tengomi() ( 一级(初级)) 信誉:100 2007-7-28 19:08:29 得分:0     ?

to division
对A中每个元素x利用二分检索检查在B中是否有元素y使得x*y=s
这个s是什么

Top   回复人:galois_godel() ( 一级(初级)) 信誉:100 2007-7-28 20:26:33 得分:0     ?

of coz 应该是O(N^2)

toold的经典题
Top   回复人:tengomi() ( 一级(初级)) 信誉:100 2007-7-28 21:04:05 得分:0     ?

nod, O(N^2)看来是不可避免的
Top   回复人:tengomi() ( 一级(初级)) 信誉:100 2007-7-28 21:04:39 得分:0     ?

to galois_godel()
请问这道题目有出处吗,谢谢
Top   回复人:medie2005(阿诺) ( 二级(初级)) 信誉:100 2007-7-28 21:11:26 得分:0     ?

我给的复杂度比较难算,那位给算算啊。


Top   回复人:oo(为了名副其实,努力学习oo技术ing) ( 两星(中级)) 信誉:110 2007-7-28 21:37:10 得分:0     ?

mark一下先
Top   回复人:division(好风长吟) ( 一级(初级)) 信誉:100 2007-7-28 22:18:04 得分:0     ?

对A中每个元素x利用二分检索检查在B中是否有元素y使得x*y=s
这个s是什么

s 即是本题中的ak
Top   回复人:tengomi() ( 一级(初级)) 信誉:100 2007-7-28 23:06:13 得分:0     ?

但是现在还不知道ak是多少啊,ai和aj也不知道啊,怎么用二分检索查?
Top   回复人:Vitin(卫亭) ( 三级(初级)) 信誉:100 2007-7-29 0:30:27 得分:0     ?

medie2005(阿诺)的算法复杂度:

1)对于ak,设需要测试的ai的个数为p,则对于每个ai,二分查找的个数为k-p。
2)假设输入是均匀分布的,那么平均情况下,p=k^(1/2),k-p = k - k^(1/2)。
3) k的取值范围是0--N,平均情况下是N/2,故第二点的p为O(N^(1/2)),k-p为O(N)。
4)查找ak次数,平均情况下为N/2
综上,该算法平均时间复杂度是O(N^(3/2)logN),小于O(N^2).

而在最坏情况下,p可以达到它的最坏复杂度O(N),并且k-p也保持O(N)[例如,当某种输入分布使p=k/2时]。此时,算法达到其最坏复杂度O(N^2*logN),大于O(N^2).

可 以对这个算法做一些改进以进一步提高速度。例如,对于二分查找方面,可以改用hash查找。预先用hash表存储所有的输入值(不需要对每个ak建立 hash表,只要一次性建立即可,因为根据对ai的限定,如果查找出值,则一定是所需要的),并在原先二分查找的地方,使用hash查找。由于建立整个 hash表的复杂度是O(N),并且整个算法只需建立一次。而查找一次hash表的复杂度是O(1)。所以,这个改进可以使算法的平均复杂度达到O(N^ (3/2)),最坏复杂度达到O(N^2)。

Top   回复人:Vitin(卫亭) ( 三级(初级)) 信誉:100 2007-7-29 0:41:00 得分:0     ?

上文有一个含糊之处,在3)中,“k-p为O (N)”系指将其个数等价于O(N),而在复杂度计算中,由于使用二分查找,其实际复杂度是O(logN)。在最坏情况分析中,"k-p也保持O(N)" 也是同样的含义。当然,在每个复杂度的结论部分没有错误,均已考虑了此处说明的内容。
Top   回复人:Vitin(卫亭) ( 三级(初级)) 信誉:100 2007-7-29 1:03:38 得分:0     ?

这个题目,最坏情况下为O(N^2)的原因是:在把每个数字看成相互独立的前提下,任何一个ai*aj(或者ak/ai)都有可能目标值,为此,最坏情况下至少需要计算每一个值,从而达到O(N^2)的复杂度。
也 许,可以想办法去除数字间的独立性,从而找到进一步提高的可能。如将每个a因式分解后再想办法查找?N个元素的因式分解的最坏复杂度是O(N^(3/2) logN),低于O(N^2)。此后如果有复杂度基于因子个数[为O(N^(1/2))]的算法可以用低于4次方的速度找到结果,那么就有可能将最坏复杂 度提高到O(N^(3/2)logN)。
Top   回复人:chenhu_doc(^0^纯一狼^0^ (国家队里,我最爱大头李玮峰!)) ( 两星(中级)) 信誉:94 2007-7-29 1:15:43 得分:0     ?

kan.
Top   回复人:roboo007(无事) ( 一级(初级)) 信誉:100 2007-7-29 5:53:20 得分:0     ?

mark
Top   回复人:oo(为了名副其实,努力学习oo技术ing) ( 两星(中级)) 信誉:110 2007-7-29 7:20:56 得分:0     ?

to Vitin(卫亭) ( )

因式分解的话复杂度跟元素的大小相关的,如果元素值比N大很多,那总的复杂度会比N^2大
Top   回复人:yyr2006() ( 一级(初级)) 信誉:100 2007-7-29 11:39:13 得分:0     ?

O(N^2)

先从小到大排序

for(i=0;i<n;i++)
{
for(j=0,k=i;j<=i && k<n;)
{
if(a[i]*a[j]==a[k])return; //find
else if(a[i]*a[j]>k)k++;
else j++;
}
}

return -1;


这个会计算出最小的那个 ak
Top   回复人:Vitin(卫亭) ( 三级(初级)) 信誉:100 2007-7-29 11:45:03 得分:0     ?

ok.
oo(为了名副其实,努力学习oo技术ing) 说的对,关于因式分解的复杂度计算错了。
对于值为V的整数,做因式分解的复杂度为O(V^(1/2)logV)。
之前我将ak的最大序数值N和最大数值V=max(ak)搞混了。

呵呵,看来因式分解是不行的。大家可以想想其他办法。
Top   回复人:galois_godel() ( 一级(初级)) 信誉:100 2007-7-29 12:33:07 得分:0     ?

请问这道题目有出处吗,谢谢
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~作过竞赛题好几次阿
Top   回复人:yyr2006() ( 一级(初级)) 信誉:100 2007-7-29 15:16:30 得分:0     ?

这是俺的解法,按俺的测试没超过 O(N^2)

bool MyFind(vector<unsigned int> &vec, unsigned int &result)
{
if(vec.size() < 3)
{
result = -1;
return false;
}

std::sort(vec.begin(), vec.end()); //小到大排序

int count = 0;
vector<unsigned int>::size_type left, right;
const vector<unsigned int>::size_type MaxRight = vec.size() - 1; //数组最大下标, 最小下标为 0

for(vector<unsigned int>::size_type mid = vec.size() - 2; mid > 0; mid--)
{
left = mid - 1;
right = MaxRight;

while(left >= 0 && right > mid)
{
count++;
if(vec[right] == vec[mid] * vec[left])
{
result = vec[right];
cout<<count<<endl;
cout<<vec[right]<<"="<<vec[mid]<<"*"<<vec[left]<<endl;
return true;
}

if(vec[right] > vec[left])
{
--right;
}
else
{
--left;
}
}
}

cout<<count<<endl;
return true;
}
Top   回复人:uglylourt(我是凡人) ( 一级(初级)) 信誉:100 2007-07-30 10:00:00 得分:0     ?
一个笨方法 O(N^3)
例 1,2,3,4,5,15,16,17,18,64
先分析 64
64 开方为 8

1,2,3,4,5 | 15,16,17,18,
------> <-----------
n1 * n2 = 64
有1 * 18 < 64 必有 n1 > 1
.....
必有 n1 > 3
4 * 18 > 64 必有 n2 < 18
....
.
.
.
得到64 = 4 * 1
Top   回复人:tengomi() ( 一级(初级)) 信誉:100 2007-07-30 10:51:16 得分:0     ?
呵呵,再看看,如果没有更好的解法,今天晚上就结贴了
Top   回复人:shareyao() ( 一级(初级)) 信誉:100 2007-07-30 10:58:54 得分:0     ?
先递增排序:a1,a2,...an
因为是递增,假定i<j<k

pseudocode:

for(k=n →1)
for(j = k-1 →1)
{
if(a[k]%a[j] != 0)
countinu;
else
binarysearch(//二分查找[1-j)之间是否存在a[k]/a[j]);

if(存在)
return a[k]; //满足条件的最大数
}

return -1;
Top   回复人:danjiewu(阿丹) ( 二级(初级)) 信誉:100 2007-07-30 12:16:50 得分:0     ?
只要求最大的ak,应该不需要O(n^2)
Top   回复人:charlie_god() ( 一级(初级)) 信誉:100 2007-07-30 12:24:19 得分:0     ?
1.把集合采用组合算法变为 3个元素对的集合{{a1, b1, c1},....{an, bn, cn} }
2.接着把这个3元素对集合进行 ak * bk == ck 检验
Top   回复人:danjiewu(阿丹) ( 二级(初级)) 信誉:100 2007-07-30 12:57:55 得分:0     ?
想了一下,复杂度确实是O(n^2)的。
Top   回复人:masterfather() ( 一级(初级)) 信誉:100 2007-07-30 13:34:33 得分:0     ?
#include <iostream.h>
void main()
{
int i,j,k,n,t;
int s=0;
cin>>n;
int *a=new int[n];
int *b=new int[n];
for(i=0;i<n;i++)
cin>>a[i];
for(i=0;i<n-2;i++)
for(j=i+1;j<n-1;j++)
for(k=j+1;k<n;k++)
if(a[i]*a[j]==a[k])
b[s++]=a[k];
if(s==0)
cout<<-1<<endl;
else
{
for(i=0;i<s-1;i++)
for(j=i+1;j<s;j++)
if(b[i]<b[j])
{
t=b[i];
b[i]=b[j];
b[j]=t;
}
cout<<b[0]<<endl;
}
}
Top   回复人:masterfather() ( 一级(初级)) 信誉:100 2007-07-30 13:36:48 得分:0     ?
我刚试过了,可以提到想要的结果。