最大子矩阵和(学习心得+AC代码)

博客围绕最大子矩阵和与最大子段和问题展开,给出了相关例题的输入输出示例。介绍了一维最大子段和的dp模板,强调一维dp数组含义。还说明了将二维最大子矩阵和转化为一维最大子段和的方法,即枚举所有行数的子矩阵并压缩列值求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

例题:无奈网上没有一篇我能看懂的题解,最后还是请教别人才过了

如果不懂一维的最大子段和,可以先看结尾-》

Description
给出一个m×n的矩阵,请输出它的最大子矩阵和
Input
多测试用例,每个测试用例:
第一行是两个正整数m和n,表示该矩阵的行数和列数。1 < m, n < 400
接下来m行,每行n个整数,空格分隔。
Output
每个测试用例输出一行:该矩阵的最大子矩阵和。运算结果在int范围内。
Sample Input
4 4
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
Sample Output
15
先给简洁代码:

#include<bits/stdc++.h>
using namespace std;
int a[401][401];
int f[401]; 
int sum[401][401];
int main(void)
{
	int n,m,i,j,maxz;
	while(~scanf("%d %d",&n,&m))
	{
		maxz=-99999999;//不能是0 
		for(i=1;i<=n;i++)
		for(j=1;j<=m;j++)
		scanf("%d",&a[i][j]);
		memset(f,0,sizeof(f));
		memset(sum,0,sizeof(sum));
		for(j=1;j<=m;j++)
		for(i=1;i<=n;i++)
		sum[i][j]=a[i][j],sum[i][j]+=sum[i-1][j];//sum是前缀和为了减少下面求第i行到i+k行的时间复杂度
		for(i=1;i<=n;i++){//子矩阵行数 
			for(int k=1;k<=n;k++){//原矩阵行数
			memset(f,0,sizeof(f));//重新初始化 
				if(i==1)
				{
					for(j=1;j<=m;j++){
						f[j]=max(f[j-1]+a[k][j],a[k][j]);
						maxz=max(maxz,f[j]);
					}
				}
				else
				{
					for(j=1;j<=m&&i+k<=n;j++){//j为开始的行数 
						int s=sum[i+k][j]-sum[k-1][j];//将这一列的数总和
						f[j]=max(f[j-1]+s,s);//转化为一维的最大子段和 
						maxz=max(maxz,f[j]); 
					}
				}
			}
		}
		printf("%d\n",maxz);
	}
	return 0;
}

由于所给数据的“坑”(百试百灵,就是AC不了),还有自己对dp的理解不深。
现在把一直调整的代码拿出来

#include<bits/stdc++.h>
using namespace std;
int a[401][401];
//int f[401][401];
int f[401]; 
int sum[401][401];
//int dp[401][401];
int main(void)
{
	int n,m,i,j,maxz;
	while(~scanf("%d %d",&n,&m))
	{
		maxz=-99999999;//不能是0 
		for(i=1;i<=n;i++)
		for(j=1;j<=m;j++)
		scanf("%d",&a[i][j]);
		memset(f,0,sizeof(f));
		//memset(dp,0,sizeof(dp));
		memset(sum,0,sizeof(sum));
		/* 
		//............错..........误..........代码....................
		for(i=1;i<=n;i++)
		for(j=1;j<=m;j++)
		sum[i][j]=a[i][j],sum[i][j]+=sum[i-1][j];
		/* 
		for(i=1;i<=n;i++)
		for(j=1;j<=m;j++)
		f[i][j]=max(f[i][j-1]+a[i][j],a[i][j]);
		-->这样只能求出该行的最长以j结尾的最大子段和,对于求最大子矩阵并没有什么卵用 */ 
		/* 
		for(i=1;i<=n;i++){
			for(j=1;j<=m;j++){
			//dp[i][j]=max(dp[i-1][j]+sum[i][j],sum[i][j]);----》sum数组记录的是1~j的和,故此dp数组只能求出从a[i][1]开始的最大子矩阵和,故WA 
			dp[i][j]=max(dp[i-1][j]+sum[i][j],f[i][j]);
			maxz=max(maxz,dp[i][j]);
		    }
		}*/ 
		// ............错..........误..........代码....................
		for(j=1;j<=m;j++)
		for(i=1;i<=n;i++)
		sum[i][j]=a[i][j],sum[i][j]+=sum[i-1][j];
		for(i=1;i<=n;i++){//子矩阵行数 
			for(int k=1;k<=n;k++){//原矩阵行数
			memset(f,0,sizeof(f));//重新初始化 
				if(i==1)
				{
					for(j=1;j<=m;j++){
						f[j]=max(f[j-1]+a[k][j],a[k][j]);
						maxz=max(maxz,f[j]);
					}
				}
				else
				{
					for(j=1;j<=m&&i+k<=n;j++){//j为开始的行数 
						int s=sum[i+k][j]-sum[k-1][j];//将这一列的数总和
						f[j]=max(f[j-1]+s,s);//转化为一维的最大子段和 
						maxz=max(maxz,f[j]); 
					}
				}
			}
		}
		printf("%d\n",maxz);
	}
	return 0;
}

能发现自己的错误,还全靠请教dalao,看来讨论是很重要的。

一维

Description
给出n个整数序列(可能为负数)组成的序列a1, a2, …, an,求该序列形如在这里插入图片描述子段和的最大值。当所有整数均为负数时,定义最大子段和为0。
Input
多测试用例。每个测试用例占2行:
第一行是序列的个数n(0 < n ≤ 10000),第二行是n个整数。
Output
为每个测试用例输出一行结果:最大子段和。
Sample Input
6
-2 11 -4 13 -5 -2
3
1 2 3
Sample Output
20
6
dp模板;

#include<bits/stdc++.h>
using namespace std;
int dp[10005];
int a[10005];
int main(void)
{
	int i,n,maxz;
	while(~scanf("%d",&n))
	{
		int flag=0;
		maxz=0;
		memset(dp,0,sizeof(dp));
		for(i=1;i<=n;i++){
			scanf("%d",&a[i]);
			if(a[i])
			flag=1;
		}
		for(int i=1;i<=n;i++){
			dp[i]=max(dp[i-1]+a[i],a[i]);//dp[i]表示以ai结尾的最大子段和 
			maxz=max(dp[i],maxz);
		}
		if(flag==0)
		printf("0\n");
		else
		printf("%d\n",maxz);
	}
}

解释就不解释了,不懂就去看看其他dalao的解释吧,给出代码是为了方便与二维的最大子矩阵和做比较

最后记住一点,一维的最大子段和dp【j】数组是表示以j结尾的最大子段和(就是说不能确定是哪个位置到j的和),所以同理当我们把二维的最大子矩阵和转化为一维的最大子段和时,你要知道子矩阵不是只能从第一行到第i行的子矩阵,也可以是第2,3,4行开始的(k来枚举),所以重点来了:我们转化为一维(f数组)是就要枚举所有行数为1,2,3,4,,i的子矩阵。最后取个max就是答案了

总之:就是把每一列从k行到i行压缩(k<i)为一个值,m个值构成一个一维的数组,对他求最大子段和

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值