您的位置:首页 > 其它

【神一样的作业】二维数组连续的二维子数组的和(元素可以连续)

2014-03-29 21:49 411 查看
[b]结对编程训练[/b]

——小组成员:刘铸辉 何晓楠

之前做二维数组连续的二维子数组的和的时候有问题,当时不是很懂,这次跟新问题一起来跟大家讨论下。

[b]扩展问题一:[/b]

[b]二维数组连续的二维子数组的和(矩形)[/b]

  首先子数组是个矩形,肯定要先遍历,而且复杂度以很高,如何遍历也是一个问题。

方法1 遍历

这个时候我们可以把每一行看成是一个元素,这样就变成了一个纵向的一维数组了。这样对一维数组的遍历是和以前做的是一样的。而对于每一行我们遍历也是和一维是一样的。编码如下:

//求二维数组的连续子数组之和的最大值
 int MaxSum(int (*array)
)
 {
    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;
 }


方法2 先求部分和

求二维的时候就变成了四个坐标了,不能遍历求和了。可以先求部分和,把他当作已知的,然后定义一个部分和数组PartSum,其中PartSum[i][[j]代表了下标(0,0),(0,j),(i,0),(i,j)包围的区间的和。而此时下标(imin,jmin),(imin,jmax),(imax,jmin),(imax,jmax)包围的区间和就等于

PartSum[imax][[jmax]-PartSum[imin-1][[jmax]-PartSum[imax][[jmin-1]+PartSum[imin-1][[jmin-1]。

这就是要求的PartSum(imin,jmin,imax,jmax),接下来就是求PartSum数组了。而对于每一个PartSum[i][[j]都不是孤立的,都是和其他的有关系的。然后找出这个关系式:

PartSum[i][[j]=PartSum[i-1][[j]+PartSum[i][[j-1]-PartSum[i-1][[j-1]+array[i][j]。

int MaxSum(int (*array)
)
{
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;
}


[b]方法3动态规划[/b]

把每一列看成一个元素。这样对于遍历的行区间,我们就可以当成一维来做。对于imin和imax之间的每一列,就相当于一维的一个元素。假设这个一维数组是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].(此时BC是一个函数)

//求二维数组的连续子数组之和的最大值
int MaxSum(int (*array)
)
{
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 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 BC(int (*PartSum)[N+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;
}


这样就算是把二维数组连续的二维子数组的和问题解决了,但是这只是求矩形子数组。

[b]扩展问题二:[/b]

[b]二维数组连续的二维子数组的和(左右相连,上下也相连)[/b]

分析思路:这个最大子矩阵和就是把平面上下相连和左右相连,无论先连哪一边都一样。先随便找一条横边和一条竖边即可,然后原问题等价于把4个这样的平面拼在一起,然后找不超过一个平面大小的最大子矩阵。先转换为1维的问题,枚举上下边界,用一维的方法用单调队列求解。编码如下:

int main()
{
int m,n;
scanf("%d%d",&m,&n);
int temp[101][101];
int result[10000];
int s[202][202];
int i,j,p,q;

memset(temp,0,sizeof(temp));
memset(s,0,sizeof(s));
memset(result,0,sizeof(result));
//输入二维数组
for( i=1; i<=m; i++)
for( j=1; j<=n; j++)
{
scanf("%d",&s[i][j]);
s[i][n+j] = s[i][j];
s[m+i][j] = s[i][j];
s[m+i][n+j] = s[i][j];
}
int r =0;
for( i=0; i<m;i++)
for( j=0; j<n;j++)
{
for( p=1; p<=m; p++)
{
for(q=1; q<=n; q++)
temp[p][q] = s[p+i][q+j];
}
result[r++] = MaxSum2(m,n,temp);   //记录各种情况下的最大子矩阵和
}

int maxresult = result[0];          //从各种情况下的最大子矩阵和中找出最大的值
for (i=1; i<m;i++)
{
if(maxresult < result[i])
maxresult = result[i];
}
printf("%d\n",maxresult);
return 0;
}

//用动态规划算法计算“最大子段和问题”,对应于一维数组
int MaxSum(int n,int* a)
{
int sum=0,b=0;
for (int i=1;i<=n;i++)
{
if (b>0) b+=a[i];
else b=a[i];
if (b>sum)  sum=b;
}
return sum;
}


int MaxSum2(int m, int n, int a[][101])
{
int sum=0;
int b[101];
int i,j,k,p,t1=0,t2=0;
memset(b,0,sizeof(b));         //内存空间初始化
for ( i=1; i<=m; i++)
{
int flag_m = m , flag_n = n;
for( k=1; k<=2*n; k++)
b[k]=0;
for ( j=i; j < flag_m + i; j++)
{
t1 = j>m? j-m : j;
for( k=1; k <=n  ; k++)
{
t2 = k>n? k-n : k;
b[k] += a[t1][t2];
}
int max = MaxSum(2*n,b);
if(max>sum) sum = max;

}
}
return sum;
}


有些思路也是从网上借鉴的,看了很多种解法,然后结合自己的解法跟大家分享下。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: