求两数组的内积最大值

求两数组的内积最大值

分类: 算法和数据结构学习   311人阅读  评论(1)  收藏  举报
c iterator system google 面试 算法

 

据说这是一道Google的面试题。在网上看到有些人在讨论这道题目,但能给出正确算法的人并不多。

问题描述:有两个长度均为n的整数数组A和B,现在要从这两个数组中各抽出s个数字,分别构成两个新的数组C和D,要求数组C和D的内积最大。

即已知:
A=[a1,a2,...,an],B=[b1,b2,...,bn];
求:
C=[c1,c2,...,c5],D=[d1,d2,...,d5];
满足:
c1,...,c5属于A,d1,...,d5属于B;
使得C、D的内积(c1*d1,c2*d2,...,c5*d5)最大。

一、我们先考虑只有正数的情况:

当s=1时,题目就退化成,从n个正整数中选取一个,从另外n个正整数中选取一个,使得乘积最大。显然,两次选取的都应该是那些数中最大的。

当s>1时,我们分两步考虑,先考虑选取哪些数,再考虑这些数怎么配对。

1. 相信很多人都可以轻松地得出这样的结论:从A中选取最大的s个数构成C,从B中选取最大的s个数构成D,才有可能使得C、D内积最大。因为如果用A中的某个较小的数替换C中的任何一个数字,都会导致对应的乘积变小,从而整个内积变小。对于D也是类似的。

2. 对于选定的C和D,如何配对呢?显然,应该让C中最大的数与D中最大的数相乘,C中第二大的数与D中第二大的数相乘,以此类推。这个命题的证明也是很简单的.

因此,如果A、B全部都是正整数,那只需要分别排序后,从大到小选取s个数即可。

二、接下来考虑只有负数的情况:

只有负数跟只有正数是类似的,因为两个负数相乘的结果与这两个负数的绝对值相乘是一样的。根据上面的分析,我们只要对A、B分别排序后,从小到大(即绝对值从大到小)选取s个数即可。

三、再考虑两个数组一个全是正数,另一个全是负数的情况:

不妨设A中全是正数,B中全是负数。

当s=1时,题目就退化成,从n个正整数中选取一个,从另外n个负整数中选取一个,使得乘积最大。显然,两次选取的都应该是那些数中绝对值最小的(即最小的正数和最大的负数)。

当s>1时,还是分两步考虑。

1. 很容易证明,应该从两个数组中分别选取绝对值最小的s个数(即正数数组中最小的s个数,负数数组中最大的s个数)。因为如果剩余的任何数字替换进来,都会导致对应的乘积的绝对值变大,乘积本身变小,从而整个内积变小。值得注意的是,很多人在这里容易出错,他们没有考虑到乘积为负数时,绝对值越大,乘积本身越小。

2. 对于选定的C和D,如何配对呢?根据上面一、2.中的式子可以知道,我们还是要让最大的那对数相乘,第二大的那对数相乘,……。然而这里需要注意,也是很多人容错一出错的地方,最大的那对数,其实是正数中最大的,负数中最小的。比如[1, 2]和[-1, -2],正确的配对应该是1*-2+2*-1=-4,而不是1*-1+2*-2=-5。

四、几种特殊情况都考虑完了,最后就是正负数任意混合的一般情况。根据上面的分析,我们终归是要对A和B分别排序的,排序之后将两个数组的下标对齐,可以将两个数组分成三个部分,第一个部分中两个的数组元素都是负数(负数部分),第二个部分中一个数组元素都是负数而另一个都是正数(异号部分),第三个部分中两个数组的元素都是正数(正数部分),如下所示:
A=[— | +  | +]

B=[— | — | +]

由于负数部分和正数部分都产生正的乘积,我们需要同时考虑这两个部分。每次从这两个部分各选出绝对值最大的一对数,将乘积更大的那对从A、B中转移到C、D中,然后继续比较。

如果负数部分和正数部分都取完了,还缺m对数,那就从异号部分选取最小的m个正数,和最大的m个负数,对应配对即可。

 

 

[c-sharp]  view plain copy
  1. #include <iostream>  
  2. #include <vector>  
  3. #include <functional>  
  4. using namespace std;  
  5. int MaxInnerProduct(int A[], int B[], int n, int m, vector<int>& C, vector<int>& D)  
  6. {  
  7.     int sum = 0;  
  8.     C.clear();  
  9.     D.clear();  
  10.     int i, j;  
  11.     sort(A, A+n, less<int>());  
  12.     sort(B, B+n, less<int>());  
  13.     for(i = 0; i< n; i++)  
  14.          cout << A[i] <<' ' << B[i] << endl;  
  15.       
  16.     int val1, val2;  
  17.     i = 0; j = n-1;  
  18.     while(C.size() < m)  
  19.     {  
  20.            val1 = A[i] * B[i];  
  21.            val2 = A[j] * B[j];  
  22.            if(val1 < 0 && val2 < 0) break;  
  23.            if(val1 >= val2)  
  24.            {  
  25.                    C.push_back(A[i]);  
  26.                    D.push_back(B[i]);  
  27.                    sum += val1;  
  28.                    i++;  
  29.            }  
  30.            else  
  31.            {  
  32.                    C.push_back(A[j]);  
  33.                    D.push_back(B[j]);  
  34.                    sum += val2;  
  35.                    j--;  
  36.            }  
  37.     }  
  38.     while( C.size() < m )   
  39.     {             
  40.         C.push_back(A[i]);   
  41.         D.push_back(B[j]);   
  42.         sum += A[i] * B[j];   
  43.         i ++ ;   
  44.         j --;   
  45.     }  
  46.     return sum;  
  47. }  
  48. int main()  
  49. {  
  50.     int A[10] = {2,4,12,-4,6,12,9,-1, -20, -10};  
  51.     int B[10] = {2,3,7,9,-10,-12,-3, -9, -10, -100};  
  52.     int n = 10, m = 9;  
  53.       
  54.     vector<int> C;  
  55.     vector<int> D;  
  56.       
  57.     cout << MaxInnerProduct(A, B, n, m, C, D) << endl;  
  58.       
  59.     vector<int>::iterator iter;  
  60.     for(iter = C.begin(); iter != C.end(); iter ++)  
  61.          cout << *iter << ' ';  
  62.     cout << endl;  
  63.     for(iter = D.begin(); iter != D.end(); iter ++)  
  64.          cout << *iter << ' ';  
  65.     cout << endl;  
  66.       
  67.     system("pause");  
  68.     return 0;  
  69. }  
 

你可能感兴趣的:(算法和数据结构学习)