题目描述:
一个n*m的矩阵,求其中不同行并且不同列的两个数的最大乘积。 2<=n,m<=1000 矩阵中为正整数。
如矩阵为:
1 2 8
4 3 6
5 4 18和6同列,所以不符合条件,最大的乘积是8*5=40
分析:
首先矩阵不大,1000*1000,一共1000000个元素,我先想到了排序,从大到小把所有元素排序,然后选择最大的两个。
这两个如果符合条件,那么就是答案,不符合的话,,,,就麻烦了
假如排完序是这样的 a1 a2 a3 a4 a5 . . . . .
你如果保留a1的话,那么可能a2 a3都会不满足,直到a4才满足,但是可能 a2*a3>a1*a4。
这样就陷入了a1到底留不留的问题,而且后面也会遇到相似的问题,用O(n^2)的复杂度枚举的话,是百万级数据显然不行。
根据不能在同一行的这个条件,我们只把每行的最大值找出来,然后再排序,再从中选择两个不同列的最大数,这样可行吗?
看起来可行,而且1000个数再用O(n^2)的复杂度枚举,也不会超时,但是求得的答案是正确的吗?
看下面几个例子:
1 1 9 1 1 1 1 9 8 1
1 1 8 1 1 1 1 3 1 1
1 1 7 1 1 1 2 1 1 1
第一个样例每行的最大值都在同一列,所以没法求答案了?
第二个样例 我们选出的每行最大数是9 3 2,因为9和3不匹配,所以答案是9*2=18。而显然8*3更大,正确答案被丢弃了?
综上所述,我们把每行的最大值找出来也是不对的,或者说是不足够的。
正解:
从上面的分析我们发现次大值也是可能对答案有贡献,但是第三大值是一定不会用到的,因为如果第i行的最大值无法与另一行的组合,那么第i行的次大值一定可以与其组合,所以不需要第三大值。
所以正解是我们先把每行的最大值和次大值找出来,然后再用O(n^2)的时间对 行与行 进行两两匹配。
对于第i行和第j行来说:
若第i行的最大值的列序号与第j行的最大值的列序号不同,则这两行乘积的最大值为两行分别的最大值相乘;
若第i行的最大值的列序号与第j行的最大值的列序号相等,说明i行的最大值的列序号与j行的第二大值得序号必不相等,同样的,j行的最大值的列序号与i行的第二大值得序号也不相等,则乘积最大值在这两个交叉的乘积中取最大的。
这样先预处理出最大值次大值再匹配的话,就可以O(n^2)解决这个题目。
代码:
#include<iostream>
using namespace std;
int a[1005]; //c[i]存储第i行的最大值
int c[1005]; //c[i]存第i行最大值的列号
int d[1005]; //d[i]存储第i行的次大值
int n,m,x;
int main(){
cin>>n>>m;
//读入并求出最大值、次大值以及最大值的列号
for (int i=1; i<=n; i++){
for (int j=1; j<=m; j++){
cin>>x;
if (x>a[i]) {
d[i]=a[i]; //a[i][j]大于最大值,要被替换,所以用它更新次大值
a[i]=x; c[i]=j; //更新最大值
}else
if (x>d[i]){
d[i]=x; //更新次大值
}
}
//cout<<a[i]<<" "<<c[i]<<" "<<d[i]<<endl;
}
//两两匹配,求出最大乘积
int ans=0;
for (int i=1; i<=n-1; i++){
for (int j=i+1; j<=n; j++){
if (c[i]!=c[j]) ans=max(ans,a[i]*a[j]);
else {
ans=max(ans,a[i]*d[j]);
ans=max(ans,d[i]*a[j]);
}
}
}
cout<<ans<<endl;
}
注意:代码中求最大值和次大值的地方要注意,如果一个数大于最大值,你不仅需要用它覆盖最大值,还需要用原来的最大值去更新次大值。来了新老大,旧的老大起码也能当个老二。
总结:
这个题目的难点在于应用不同行不同列这个条件
根据不同行的原则,我们把每行单独看
对于行与行之间的最大值,要考虑周全,如果列序号不同怎么办,相同又怎么办
把这些问题一步步解决,那么这个题目的解法也就自然出来了。