[蓝桥杯][历届试题]最大子阵

题目描述:
给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大。
其中,A的子矩阵指在A中行和列均连续的一块。
样例说明
取最后一列,和为10。

输入:
输入的第一行包含两个整数n, m,分别表示矩阵A的行数和列数。
接下来n行,每行m个整数,表示矩阵A。
数据规模和约定
对于100%的数据,1< =n, m< =500,A中每个元素的绝对值不超过5000。

输出:
输出一行,包含一个整数,表示A中最大的子矩阵中的元素和。

样例输入:
3 3
-1 -4 3
3 4 -1
-5 -2 8

样例输出:
10

**注意:**当出现矩阵元素全为负数时会出错,s=0,会将s赋给max,故得出的结果为0
并且这种方法不可取,不能仅根据某一列的和为负就将其丢弃,因为有可能出现这种情况:
在这里插入图片描述
不能因为第三列的和为负就将其丢弃,令s=0
而应继续往后加,最大应为3+4-2+10而不是丢弃-2后令s=0得到答案10

错误代码:

#include <bits/stdc++.h>
#define MAX 501

using namespace std;

int main()
{
	int a[MAX][MAX]={0};
	int sum[MAX][MAX]={0};       //sum[i][j]保存数组中第j列第一个到第i个元素的和
	int n,m;
	int i,j,c;
	long long max=0,temp,s=0;
	
	cin>>n>>m;
	for(i=1;i<=n;++i)
	   for(j=1;j<=m;++j)
	      cin>>a[i][j];
	
	for(i=1;i<=n;++i)
	   for(j=1;j<=m;++j)
	       sum[i][j]=sum[i-1][j]+a[i][j];

	max=a[1][1];
	for(i=1;i<=n;++i)
	    for(j=i;j<=n;++j)
		{
			s=0;                  //当为最后一列时不能再往后加了,要令s为零 
			for(c=1;c<=m;++c)
			{	
				temp=sum[j][c]-sum[i-1][c];
				if(temp>=0)      //判断条件必须为>=,有可能出现这种情况: 
				{   s+=temp;     //第一列和>0,第二列和=0,第三列和>0 
				    if(s>max)
				       max=s;    //有可能矩阵全部元素都大于0
			    } 
				else
				{
					if(s>max)
					  max=s;
					s=0;
				}
				/*s+=sum[j][c]-sum[i-1][c];
				if(s>max)
				   max=s;
				if(s<0)
				   s=0;*/
			}     
	    }
			
	cout<<max<<endl;
}

正确代码:

#include <bits/stdc++.h>
#define MAX 501

using namespace std;

int main()
{
	int a[MAX][MAX]={0};
	int sum[MAX][MAX]={0};       //sum[i][j]保存数组中第j列第一个到第i个元素的和
	int n,m;
	int i,j,c;
	long long max=0,temp,s=0;
	
	cin>>n>>m;
	for(i=1;i<=n;++i)
	   for(j=1;j<=m;++j)
	      cin>>a[i][j];
	
	for(i=1;i<=n;++i)
	   for(j=1;j<=m;++j)
	       sum[i][j]=sum[i-1][j]+a[i][j];

	max=a[1][1];
	for(i=1;i<=n;++i)
	    for(j=i;j<=n;++j)
		{
			s=0;                  //当为最后一列时不能再往后加了,要令s为零 
			for(c=1;c<=m;++c)
			{	
				s+=sum[j][c]-sum[i-1][c];
				if(s>max)         
				   max=s;          //每次都对max进行更新 
				   
				if(s<0)            //当前面几列的和小于零时再加一列和得到的答案
				   s=0;            //会比只取那一列的和所得到的答案小,即s+s1<s1 
			}                      //故需要更新,即s=0 
	    }
			
	cout<<max<<endl;
}

改进代码一边输入a[i][j]一边将得到的j列第一个到第i个元素的和保存再数组a中,即将本应保存在数组sum中的数保存在数组a中

#include <bits/stdc++.h>
#define MAX 501

using namespace std;

int main()
{
	int a[MAX][MAX]={0};
	int n,m;
	int i,j,c;
	long long max=0,temp,s=0;
	
	cin>>n>>m;
	for(i=1;i<=n;++i)
	   for(j=1;j<=m;++j)
	    {
		    cin>>a[i][j];
		    a[i][j]+=a[i-1][j];
	    }

	max=a[1][1];
	for(i=1;i<=n;++i)
	    for(j=i;j<=n;++j)
		{
			s=0;                  //当为最后一列时不能再往后加了,要令s为零 
			for(c=1;c<=m;++c)
			{	
				s+=a[j][c]-a[i-1][c];
				if(s>max)             
				   max=s;          //每次都对max进行更新 
				   
				if(s<0)            //当前面几列的和小于零时再加一列和得到的答案
				   s=0;            //会比只取那一列的和所得到的答案小,即s+s1<s1 
			}                      //故需要更新,即s=0 
	    }
			
	cout<<max<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值