最大子段问题的延伸——最大子矩阵[动态规划经典问题]-C语言描述

问题简述

给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大。

其中,A的子矩阵指在A中行和列均连续的一块。

样例说明
取最后一列,和为10。
数据规模和约定
对于100%的数据,1< =n, m< =500,A中每个元素的绝对值不超过5000。

输入
输入的第一行包含两个整数n, m,分别表示矩阵A的行数和列数。
接下来n行,每行m个整数,表示矩阵A。
输出
输出一行,包含一个整数,表示A中最大的子矩阵中的元素和。
样例输入

3 3
-1 -4 3
3 4 -1
-5 -2 8

样例输出

10
原理其实跟求一段数字的最大子串问题很像,关键步骤都是sum<0时候的清零

#include<stdio.h>
long long n,m,num[520][520],ans;
int main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			 scanf("%lld",&num[i][j]);
			num[i][j]+=num[i][j-1];//求行的逐个的总和 
		}
	}
	

		ans=num[1][1];
		for(int i=1;i<=m;i++){//因为是以行的总和,所以数m,如果是列那就是n
		//第一层的i负责制造左边界 
			for(int j=i;j<=m;j++){//j=i跟j++负责 制造右边界 
				long long s=0;
				for(int k=1;k<=n;k++){//k负责制造上下边界(当s加到负数之后,自动隔开变成新的上边界 
				//在这一层循环之中只考虑j到i-1列不变的情况下,1到n行之间,总和最大的那几行 
					s+=num[k][j]-num[k][i-1];//第k行的第j个减去第i个(i从0长到m)而j一直是i
					//(每完成一趟循环之后j也会递增) 
					if(s>ans) ans=s;
					if(s<0) s=0; 
				}
			} 
			
		}
		
		
	
	
	printf("%lld\n",ans);// 
	return 0;
}

以行为计算单位执行,接下来看看以列为计算单位的,原理是一样的,但是从另一个角度计算,两者无明显差异,时间复杂度上理论上是一样的,但是根据实际的m、n值可能是不同的

#include<iostream>

#include<stdio.h>
long long  n,m,sum, ans,num[520][520];
int main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			scanf("%lld",&num[i][j]);
			num[i][j]=num[i-1][j]+num[i][j];//这里是纵列上的相加 
			//这里为了方便,不考虑输入进来全是负数的情况 
		}
	}
	
	ans=num[1][1];//设立一个极小值 
	for(int i=1;i<=n;i++) {//i负责构成上边界 
		for(int j=i;j<=n;j++){//j负责构成下边界 
			sum=0;//每次清0
			for(int k=1;k<=m;k++){
				sum=sum+num[j][k]-num[i-1][k];//k的更新负责左右边界
				if(sum>ans) ans=sum;
				if(sum<0) sum=0;//如果小于0了直接开始新的左边界并清零 
			} 
		}
	} 
	printf("%lld\n",ans);
	return 0; 
	
} 

该说明的都在注释上了,也是给自己的一个备忘

发布了5 篇原创文章 · 获赞 1 · 访问量 373

猜你喜欢

转载自blog.csdn.net/weixin_43218670/article/details/104271817