最大子矩阵:二维数组的最大连续子数组和

上篇讲了一维的连续子数组和的最大值(编程之美错误分析),下面来分析二维数组的最大子数组和,亦称最大子矩阵,
最大子矩阵:二维数组的最大连续子数组和_第1张图片

穷举法:
 //求二维数组的连续子数组之和的最大值
  int MaxSum(int (*array)[N])
  {
  	int i,j;
  	int MaxSum=-INFINITY;//初始化
  	int imin,imax,jmin,jmax;
  	for(imin=1;imin<=N;imin++)//行的最小值
      {
         for(imax=imin;imax<=N;imax++)//行的最大值
         {
  		for(jmin=1;jmin<=M;jmin++)//列的最小值
              {
  			for(jmax=jmin;jmax<=M;jmax++)//列的最大值
  				MaxSum=MaxNum(MaxSum,PartSum(imin,jmin,imax,jmax));//计算子矩阵的部分和
  		}
         }
      }			
  	return MaxSum;
  }
时间复杂度(N^2*M^2*O(PartSum)),如何求部分和PartSum呢?我们可以先求子矩阵的部分和,利用已经求出的部分和,来解出当前要求的矩阵的部分和,这个时候遍历求的时候复杂度就是O(1)我们定义一个部分和数组PartSum,其中PartSum[i][[j]代表了下标(00)(0j)(i0)(ij)包围的区间的和。
最大子矩阵:二维数组的最大连续子数组和_第2张图片

而此时下标(iminjmin)(iminjmax)(imaxjmin)(imaxjmax)包围的区间和就等于
PartSum[imax][[jmax]-PartSum[imin-1][[jmax]-PartSum[imax][[jmin-1]+PartSum[imin-1][[jmin-1]。如下图:
最大子矩阵:二维数组的最大连续子数组和_第3张图片

这就是我们要求的PartSum(imin,jmin,imax,jmax),接下来就是求PartSum数组了。如何求呢?对于每一个PartSum[i][[j]都不是孤立的,都是和其他的有关系的。我们要找出这个递推关系式
最大子矩阵:二维数组的最大连续子数组和_第4张图片
PartSum[i][[j]=PartSum[i-1][[j]+PartSum[i][[j-1]-PartSum[i-1][[j-1]+array[i][j]这样可以求出全部的PartSum[i][[j]。
  int PartSum[N+1][M+1];
  	int i,j;
  	for(i=0;i<=N;i++)
  		PartSum[i][0]=0;
  	for(j=0;j<=M;j++)
  		PartSum[0][j]=0;
  	for(i=1;i<=N;i++)
  		for(j=1;j<=M;j++)
  		   PartSum[i][j]=PartSum[i-1][j]+PartSum[i][j-1]-PartSum[i-1][j-1]+array[i-1][j-1];
OK,求得部分和之后,下标(imin,jmin)(imin,jmax),(imax,jmin),(imax,jmax)包围的区间和为:
PartSum[imax][[jmax]-PartSum[imin-1][[jmax]-PartSum[imax][[jmin-1]+PartSum[imin-1][[jmin-1]
//求二维数组的连续子数组之和的最大值
int MaxSum(int (*array)[N])
{
	int PartSum[N+1][M+1];
	int i,j;
	for(i=0;i<=N;i++)
		PartSum[i][0]=0;
	for(j=0;j<=M;j++)
		PartSum[0][j]=0;
	for(i=1;i<=N;i++)
		for(j=1;j<=M;j++)
			PartSum[i][j]=PartSum[i-1][j]+PartSum[i][j-1]-PartSum[i-1][j-1]+array[i-1][j-1];
	int MaxSum=-INFINITY;//初始化
	int imin,imax,jmin,jmax;
	for(imin=1;imin<=N;imin++)
		for(imax=imin;imax<=N;imax++)
			for(jmin=1;jmin<=M;jmin++)
				for(jmax=jmin;jmax<=M;jmax++)
						MaxSum=MaxNum(MaxSum,PartSum[imax][jmax]-PartSum[imin-1][jmax]-PartSum[imax][jmin-1]+PartSum[imin-1][jmin-1]);
						
	return MaxSum;
}
时间复杂度是O(N^2*M^2)这个复杂度还可以继续降低,DP来做,我们把每一列看成一个元素,这样对于遍历的行区间,我们就可以当成一维来做。
最大子矩阵:二维数组的最大连续子数组和_第5张图片

对于iminimax之间的每一列,就相当于一维的一个元素。假设这个一维数组是BC,则BC[j]=array[imin][j]+....+array[imax][j],问题就变成了求BC数组的连续子数组之和的最大值了。而根据刚才求的部分和我们可以知道对于imin行和imax行之间的区间第j列的值是 BC(PartSum,imin,imax,j)=PartSum[imax][j]-PartSum[imin-1][j]-PartSum[imax][j-1]+PartSum[imin-1][j-1]
// MaxMatrix.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <limits>
using namespace std;

#define N 4
#define M 3

int BC(int (*PartSum)[M+1],int imin,int imax,int j) //imin--imax第j列的和
{
	int value;
	value=PartSum[imax][j]-PartSum[imin-1][j]-PartSum[imax][j-1]+PartSum[imin-1][j-1];
	return value;
}

//求二维数组的连续子数组之和的最大值
int MaxSum(int (*array)[M])
{
	int PartSum[N+1][M+1];
	int i,j;
	for(i=0;i<=N;i++)
		PartSum[i][0]=0;
	for(j=0;j<=M;j++)
		PartSum[0][j]=0;
	for(i=1;i<=N;i++)
		for(j=1;j<=M;j++)
			PartSum[i][j]=PartSum[i-1][j]+PartSum[i][j-1]-PartSum[i-1][j-1]+array[i-1][j-1];
	int MaxSum=INT_MIN;
	int Start,All;
	int imin,imax;
	for(imin=1;imin<=N;imin++)
	{
		for(imax=imin;imax<=N;imax++)
		{
			Start=BC(PartSum,imin,imax,M);
			All=BC(PartSum,imin,imax,M);
			for(j=M-1;j>=1;j--)
			{
				if(Start>0)
					Start+=BC(PartSum,imin,imax,j);
				else
					Start=BC(PartSum,imin,imax,j);
				if(Start>All)
					All=Start;
			}
			if(All>MaxSum)
				MaxSum=All;
		}
	}
	return MaxSum;
}


int _tmain(int argc, _TCHAR* argv[])
{
	int a[N][M]={
		1,2,3,
		4,0,-2,
		-8,2,2,
		9,3,-4
	};
	int maxSum=MaxSum(a);
	cout<<maxSum<<endl;
	getchar();

	return 0;
}
时间复杂度降到O(N*N*min(M,N)),差不多O(N^3)吧。

你可能感兴趣的:(动态规划,最大子矩阵,最大连续子数组和)