求矩阵中不同行不同列的两个数的最大乘积

题目描述:

一个n*m的矩阵,求其中不同行并且不同列的两个数的最大乘积。  2<=n,m<=1000 矩阵中为正整数。

如矩阵为:

         1 2 8
         4 3 6
         5 4 1       

8和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;		
} 

注意:代码中求最大值和次大值的地方要注意,如果一个数大于最大值,你不仅需要用它覆盖最大值,还需要用原来的最大值去更新次大值。来了新老大,旧的老大起码也能当个老二。

总结:

这个题目的难点在于应用不同行不同列这个条件

根据不同行的原则,我们把每行单独看

对于行与行之间的最大值,要考虑周全,如果列序号不同怎么办,相同又怎么办

把这些问题一步步解决,那么这个题目的解法也就自然出来了。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值