码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2016-07-13 17:09:35      阅读:194      评论:0      收藏:0      [点我收藏+]

标签:

题目:如何求出一个二维数组中的最大子数组之和。
方案一:暴力破解-枚举法。对于一个二维数组我们列举出每一个子数组值的大小,然后进行比较,这样就可以得到最大的和了。其时间复杂度为: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;
}  


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

标签:

原文地址:http://blog.csdn.net/gogokongyin/article/details/51889814

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!