数字之魅:子数组之和的最大值[二维]+[三维]

题目:如何求出一个二维数组中的最大子数组之和。

方案一:暴力破解-枚举法。对于一个二维数组我们列举出每一个子数组值的大小,然后进行比较,这样就可以得到最大的和了。其时间复杂度为:O(N*N*M*M*Sum的时间复杂度)[N表示行数,M表示列数,Sum是求解子矩阵的和]。由于Sum函数求和也是采用循环,足见这个时间复杂度可是相当的大。

方案二:先计算出以左上角的元素(1,1)和当前元素(i,j)为顶点对的子矩阵的部分和,部分和的计算如下图(一)所示,我们可以看出:以(i_min,j_min),(i_min,j_max),(i_max,j_min),(i_max,j_max)为顶点的矩形区域的元素和为:Sum[i_max][j_max] = Sum[i_max][j_max]-Sum[i_min-1][j_max]-Sum[i_max][j_min-1]+Sum[i_min-1][j_min-1]。也就是在已知部分和的基础上就可以在0(1)的时间内得到任意矩形的元素之和。

那么如何得到部分和呢?

如图二所示,在更小的“部分和”基础上,也能以O(1)得到新的“部分和”。公式为:Sum[i][j]=Sum[i-1][j]+Sum[i][j-1]-Sum[i-1][j-1]+arr[i][j] 这里的arr[i][j]是矩阵中的arr[i][j]的值。因此在这里我们可以用O(N*M)来得到部分和。这里我们主要去掉了计算法子矩阵和的时间复杂度,所以其时间复杂度为:O(N*N*M*M).相比与方案一有所提高。

图一                                                                         图二

方案三:其实我们仔细的想一想,就可以发现,它的最简单的原型是求一维数组中的最大值。那么基于这个启发,我们可以把问题从二维转化为一维以提高算法性能。假设已经确定了矩阵区域的上下边界,知道矩阵区域的上下边界分布是第a行和第c行,就把每列的第a行和第c行之间的元素看成一个整体。即求数组:(merge[1],merge[2],merge[3]....).merge[i]=arr[a][i]+arr[b][i]+..+arr[c][i].

这样我们可以枚举矩形的上下边界,然后再用一维情况下的方法确定左右边界,相当于一维数组中的一个元素(通过子矩阵部分和可以在O(1)时间内计算出整体之和),就可以得到二维问题的解。新方法的时间复杂度为:O(N*N*M)=O(N*N*N)。具体过程如下图三所示:

图三

方案一的具体实现代码:

int arr[N][M];
int Max(int x,int y)//得到最大值;
{
	return  (x>y)?x:y;
}
int Sum(int imin,int imax,int jmin,int jmax)//得到子矩阵的和,时间复杂度为O(N*N);
{
	int sum=0;
	for(int i=imin;i<=imax;i++)
		for(int j=jmin;j<jmax;j++)
			sum+=arr[i][j];
	return sum;
}
int MaxSum(int *arr,int m,int n) //得到子矩阵的最大值;
{
	int maximum=0;
	for(int i_min=1;i_min<=n;i_min++)
		for(int i_max=i_min;i_max<=n;i_max++)
			for(int j_min=1;j_min<=m;j_max++)
				for(int j_max=j_min;j_max<=m;j_max++)
					maximum=Max(maximum,Sum(i_min,i_max,j_min,j_max));
	return maximum;
}

方案二的具体实现代码:

int arr[N][M];
int Sum[N][M];//存储部分和的数组;
int Max(int x,int y)//得到最大值;
{
	return  (x>y) ? x:y;
}
void GetSum()//预处理得到部分和;
{
	for(int i=0;i<=n;i++)
		Sum[i][0]=0;//边界值;
	for(int j=0;j<=n;j++)
		Sum[0][j]=0;//边界值;真正的部分和数据是从Sum[1][1]开始的;
	for(i=1;i<=n;i++)
		for(j=1;j<=m;j++)
			Sum[i][j]=Sum[i-1][j]+Sum[i][j-1]-Sum[i-1][j-1]+arr[i][j]
}
inline int Sum(int i_min,int i_max,int j_min,int j_max)//调用比较频繁,可设置为内联函数,提高效率。和方案一相比,其时间复杂度为O(1).
{
	return Sum[i_max][j_max]-Sum[i_min-1][j_max]-Sum[i_max][j_min-1]+Sum[i_min-1][j_min-1];
}
int MaxSum(int *arr,int m,int n)
{
	int maximum=0;
	for(int i_min=1;i_min<=n;i_min++)
		for(int i_max=i_min;i_max<=n;i_max++)
			for(int j_min=1;j_min<=m;j_max++)
				for(int j_max=j_min;j_max<=m;j_max++)
					maximum=Max(maximum,Sum(i_min,i_max,j_min,j_max));
	return maximum;
}

方案三的具体实现代码:

#include <iostream>
#include <algorithm>
using namespace std;
#define LEN 888
int arr[LEN][LEN];
long long Sum[LEN][LEN];  

inline long long MatrixSum(int s, int t, int i, int j)
{
	return Sum[i][j]-Sum[i][t-1]-Sum[s-1][j]+Sum[s-1][t-1];
}  

int main()
{
	int row, col, i, j;
	cout<<"please input the row and col of the array:"<<endl;
	cin >> row >> col;
	cout<<"please input the data of the array:"<<endl;
	for (i=1; i<=row; i++)  //输入数据;
		for (j=1; j<=col; j++)
			cin >> arr[i][j];  

	for (i=0; i<=row; i++)  //设置边界线;
		Sum[i][0] = 0;
	for (j=0; j<=col; j++)
		Sum[0][j] = 0;  

	for (i=1; i<=row; i++)  // 预处理计算矩阵的部分和
		for (j=1; j<=col; j++)
			Sum[i][j] = arr[i][j]+Sum[i-1][j]+Sum[i][j-1]-Sum[i-1][j-1];  

	int a, c;
	long long MaxSum = arr[1][1]; //初始值的设置;
	for (a=1; a<=row; a++)
		for (c=a; c<=col; c++)  // 将子矩阵上下边界设为第a行和第c行,求取取最大值
		{
			long long Tail = MatrixSum(a, 1, c, 1);  //合并列;
			for (j=2; j<=col; j++)  //按一维数组向后枚举;
			{
				Tail = max( MatrixSum(a, j, c, j), MatrixSum(a, j, c, j)+Tail);
				MaxSum = max(Tail, MaxSum);
			}
		}  

		cout <<"最大的子矩阵和为:"<< MaxSum<<endl;
		system("pause");
		return 0;
}  

运行结果如下:

扩展问题1:如果二维数组也是首尾相连,像一条首尾相连的带子,算法会如何改变?

其实现代码如下:

#include <iostream>
#include <algorithm>
using namespace std;
#define LEN 1003
int arr[LEN][LEN];
long long Sum[LEN][LEN];  

inline long long MatrixSum(int s, int t, int i, int j)
{
	return Sum[i][j]-Sum[i][t-1]-Sum[s-1][j]+Sum[s-1][t-1];
}  

int main()
{
	int  row, col, i, j;
	cout<<"please input the row and col of the array:"<<endl;
	cin >> row >> col;
	cout<<"please input the data of the array:"<<endl;
	for (i=1; i<=row; i++)
		for (j=1; j<=col; j++)
			cin >> arr[i][j];
	for (i=0; i<=row; i++)
		Sum[i][0] = 0;
	for (j=0; j<=col; j++)
		Sum[0][j] = 0;
	// 计算矩阵的部分和
	for (i=1; i<=row; i++)  //计算部分和;
		for (j=1; j<=col; j++)
			Sum[i][j] = arr[i][j]+Sum[i-1][j]+Sum[i][j-1]-Sum[i-1][j-1];
	int a, c;
	long long MaxSum = arr[1][1];  	// 上下边界不会跨过第n行和第1行
	for (a=1; a<=row; a++)
		for (c=a; c<=row; c++)
		{
			// 将子矩阵上下边界设为第a行和第c行
			// 左右边界不会跨过第m列和第1列
			long long Tail = MatrixSum(a, 1, c, 1);
			for (j=2; j<=col; j++)
			{
				Tail = max(MatrixSum(a, j, c, j),
					MatrixSum(a, j, c, j)+Tail);
				MaxSum = max(Tail, MaxSum);
			}
			long long Sum = MatrixSum(a, 1, c, 1);  // 左右边界会跨过第n列和第1列
			long long Start = Sum;
			int sind = 1;
			for (i=2; i<=col; i++)
			{
				Sum += MatrixSum(a, i, c, i);
				if (Sum > Start) {Start = Sum; sind = i;}
			}
			Tail = MatrixSum(a, col, c, col);
			int tind = col;
			for (j=col-1; j>=1; j--)
			{
				Sum += MatrixSum(a, j, c, j);
				if (Sum > Tail) {Tail = Sum; tind = j;}
			}
			if (sind<tind && Start+Tail>MaxSum)
				MaxSum = Start+Tail;
		}
		cout <<"最大的子矩阵和为:"<< MaxSum<<endl;
		system("pause");
		return 0;
}  

扩展问题2:求3维矩阵,也就是长方体中子长方体的最大值?

长方体我们需要求子长方体的最大值,那么可以想象一下!其实思路和二维是一样的,主要是采取降维的思路,这样可以把复杂的问题简单化。主要关键还是在求部分和,其求解公式如下:

   PS[i][j][k] = A[i][j][k]+PS[i-1][j][k]+PS[i][j-1][k]+PS[i][j][k-1]-PS[i-1][j-1][k]-PS[i-1][j][k-1]-PS[i][j-1][k-1]+PS[i-1][j-1][k-1];  [i,j,k形象点儿可分别代表其长宽高]

其时间复杂度为:O(N*N*M*M*Q)=O(N^5)。可以得出:一维是O(N),二维是O(N*N),三维是O(N^5).

具体实现代码如下:

#include <iostream>
#include <algorithm>
using namespace std;  

#define LEN 500
int arr[LEN][LEN][LEN];
int Sum[LEN][LEN][LEN];  

inline int MaxCubeSum(int a, int b, int c, int d, int i, int j)
{
	return Sum[b][d][j]-Sum[a-1][d][j]-Sum[b][c-1][j]-Sum[b][d][i-1]+
		Sum[a-1][c-1][j]+Sum[a-1][d][i-1]+Sum[b][c-1][i-1]-Sum[a-1][c-1][i-1];
}
int main()
{
	int row, col, high, i, j, k;
	cout<<"please input the row, col and high of the array:"<<endl;
	cin >> row >> col >>high;
	cout<<"please input the data of the array:"<<endl;
	for (i=1; i<=row; i++)
		for (j=1; j<=col; j++)
			for (k=1; k<=high; k++)
				cin >> arr[i][j][k];
	for (i=0; i<=row; i++)
		for (j=0; j<=col; j++)
			Sum[i][j][0] = 0;
	for (i=0; i<=row; i++)
		for (k=0; k<=high; k++)
			Sum[i][0][k] = 0;
	for (j=0; j<=col ; j++)
		for (k=0; k<=high; k++)
			Sum[0][j][k] = 0;
	// 计算长方体的部分和
	for (i=1; i<=row; i++)
		for (j=1; j<=col; j++)
			for (k=1; k<=high; k++)
				Sum[i][j][k] = arr[i][j][k]+Sum[i-1][j][k]+Sum[i][j-1][k]+Sum[i][j][k-1]-Sum[i-1][j-1][k]-Sum[i-1][j][k-1]-Sum[i][j-1][k-1]+Sum[i-1][j-1][k-1];
	int a, b, c, d;
	int MaxSum = arr[1][1][1];
	// 限制第一维的取值范围
	for (a=1; a<=row; a++)
		for (b=a; b<=row; b++)
			// 限制第二维的取值范围
			for (c=1; c<=col; c++)
				for (d=c; d<=col; d++)
				{
					// 只剩下最后一维没有确定,利用一维部分和的方法
					int Tail = MaxCubeSum(a,b,c,d,1,1);
					for (j=2; j<=k; j++)
					{
						int cur = MaxCubeSum(a,b,c,d,j,j);
						Tail = max(Tail+cur, cur);
						MaxSum = max(Tail, MaxSum);
					}
				}
				cout <<"最大的子矩阵和为:"<< MaxSum<<endl;
				system("pause");
				return 0;
}  

时间: 2024-10-01 10:33:36

数字之魅:子数组之和的最大值[二维]+[三维]的相关文章

第2章 数字之魅——子数组之和的最大值(二维)

子数组之和的最大值(二维) 问题描述 我们在前面分析了一维数组之和的最大值问题,那么如果是二维数组又该如何分析呢? 分析与解法 最直接的方法,当然就是枚举每一个矩形区域,然后再求这个矩形区域中元素的和. [解法一] 完整代码如下: 1 package chapter2shuzizhimei.maxsumsubarraytwodimensional; 2 /** 3 * 求数组的子数组之和的最大值(二维) 4 * [解法一] 5 * @author DELL 6 * 7 */ 8 public c

第2章 数字之魅——求数组的子数组之和的最大值

求数组的子数组之和的最大值 问题描述 分析与解法 [解法一] 具体代码如下: 1 package chapter2shuzizhimei.maxsumsubarray; 2 /** 3 * 求数组的子数组之和的最大值 4 * [解法一] 5 * @author DELL 6 * 7 */ 8 public class MaxSumSubArray1 { 9 //求数组的子数组之和的最大值 10 public static double maxSum(double a[]){ 11 double

编程之美 2.14求数组的子数组之和的最大值

对于一个有N个元素的数组,a[0]~a[n-1],求子数组最大值. 如:数组A[] = [−2, 1, −3, 4, −1, 2, 1, −5, 4],则连续的子序列[4,−1,2,1]有最大的和6. 方法一:暴力 循环遍历,输出所有,判断最大的和 1 #include"iostream" 2 #define MAX 1001 3 using namespace std; 4 5 int main(){ 6 int n, a[MAX], sum , maxsum ; 7 8 cin &

编程之美之2.14 求数组的子数组之和的最大值

[题目] 一个有N个整数元素的一维数组(A[0],A[1],A[2],...A[n-1]),这个数组中当然有很多子数组,那么子数组之和的最大值是多少? 该子数组是连续的. 我们先来明确一下题意: (1)子数组意味着是连续的. (2)题目只需要求和,并不需要返回子数组的具体位置. (3)数组的元素是整数,所以数组可能包含正整数,负整数或者零. 举几个例子: 数组:[1,-2,3,5,-3,2]返回8 数组:[0,-2,3,5,-1,2]返回9 数组:[-9,-2,-3,-5,-3]返回8 [解法一

编程之美2.14 求数组的子数组之和的最大值

问题描述: 一个有N个整数元素的一维数组(A[0], A[1], A[2],...,A[n-1]),这个数组当然有很多子数组,那么子数组之和的最大值是什么呢? 解法: 1. 暴力解法-------O(N^3) 2. 改进版暴力解法-------O(N^2) *3. 分治算法-------O(NlogN)(暂时未去实现) 4. 数组间关系法-------O(N) 具体思路和代码: 1.暴力解法 思路:Sum[i,...,j]为数组第i个元素到第j个元素的和,遍历所有可能的Sum[i,...,j].

求数组的子数组之和的最大值?

自己写的代码考虑未周全,引入了额外的空间复杂度: //求数组的子数组之和的最大值 #include <iostream> #define N 12 using namespace std; int main() { //int a[]={-5,2,3,-3,-2,3,1,-5}; //int a[]={-5,2,0,3,-2,3,4,5}; int a[]={1,-2,3,10,-4,7,2,-5}; int flag,max,i,j=0; int sum[N]={0}; //(1)记录子数组

编程之美读书笔记2.14 - 子数组之和的最大值

http://blog.csdn.net/pipisorry/article/details/39083281 问题: 1. 一个由N个整数元素的一维数组,求其所有子数组中元素和的最大值. 2. 如果数组首尾相邻,也就是允许子数组A[i],...,A[n-1],A[0],...,A[j]存在,求其所有子数组总元素和的最大值. 解法1: /* O(n^2) 遍历算法 */ static int maxSubarraySum1(int *a,int a_len){ int max_sum = INT

2.14 求数组的子数组之和的最大值

题目:给定一个一维数组,求这个数组的子数组之和的最大值. 最佳方法:动态规划! 一. 可以将一个大问题(N个元素数组)转化为一个较小的问题(N-1个元素数组).. 假设已经知道(A[1],...A[n-1])中最大的子数组的和为:All[1] 并且已经知道(A[1],...A[n-1])中包括A[1]的子数组的最大和为start[1] 所以最终的解All[0] = max(A[0], A[0]+start[1], All[1]) 所以通过DP来求解! 代码如下: #include <iostre

2.15 子数组之和的最大值(二维)

题目:给定一个二维数组,求这个二维数组的子数组之和的最大值. 分析:将二维降低到一维的情况. 代码: #include <iostream> const int MAXN = 10000; const int INF = 100000000; using namespace std; int B[MAXN][MAXN], PS[MAXN][MAXN]; int n, m; int BC(int first, int last, int liehao) { return PS[last][lie