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

结对编程训练

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

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

 

扩展问题一:

二维数组连续的二维子数组的和(矩形)

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

方法1 遍历

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

 1 //求二维数组的连续子数组之和的最大值  
 2  int MaxSum(int (*array)[N])  
 3  {  
 4     int i,j;  
 5     int MaxSum=-INFINITY;//初始化  
 6     int imin,imax,jmin,jmax;  
 7     for(imin=1;imin<=N;imin++)  
 8          {  
 9         for(imax=imin;imax<=N;imax++)//当成是遍历纵向的一维  
10              {  
11             for(jmin=1;jmin<=M;jmin++)  
12                  {  
13                      for(jmax=jmin;jmax<=M;jmax++)//当成是遍历横向的一维  
14                         MaxSum=MaxNum(MaxSum,PartSum(imin,jmin,imax,jmax));  
15                  }  
16            }  
17          }            
18     return MaxSum;  
19  }  

方法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]

 1 int MaxSum(int (*array)[N])  
 2         {  
 3             int PartSum[N+1][M+1];  
 4             int i,j;  
 5             for(i=0;i<=N;i++)  
 6                 PartSum[i][0]=0;  
 7             for(j=0;j<=M;j++)  
 8                 PartSum[0][j]=0;  
 9             for(i=1;i<=N;i++)  
10                 for(j=1;j<=M;j++)  
11                     PartSum[i][j]=PartSum[i-1][j]+PartSum[i][j-1]-PartSum[i-1][j-1]+array[i-1][j-1];  
12             int MaxSum=-INFINITY;//初始化  
13             int imin,imax,jmin,jmax;  
14             for(imin=1;imin<=N;imin++)  
15                 for(imax=imin;imax<=N;imax++)  
16                     for(jmin=1;jmin<=M;jmin++)  
17                         for(jmax=jmin;jmax<=M;jmax++)  
18                             MaxSum=MaxNum(MaxSum,PartSum[imax][jmax]-PartSum[imin-1][jmax]-PartSum[imax][jmin-1]+PartSum[imin-1][jmin-1]);  
19                           
20     return MaxSum;  
21 }  

方法3动态规划

把每一列看成一个元素。这样对于遍历的行区间,我们就可以当成一维来做。对于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是一个函数)

 1     //求二维数组的连续子数组之和的最大值  
 2 int MaxSum(int (*array)[N])  
 3 {  
 4     int PartSum[N+1][M+1];  
 5     int i,j;  
 6     for(i=0;i<=N;i++)  
 7         PartSum[i][0]=0;  
 8     for(j=0;j<=M;j++)  
 9         PartSum[0][j]=0;  
10     for(i=1;i<=N;i++)  
11         for(j=1;j<=M;j++)  
12             PartSum[i][j]=PartSum[i-1][j]+PartSum[i][j-1]-PartSum[i-1][j-1]+array[i-1][j-1];  
13     int MaxSum=-INFINITY;  
14     int Start,All;  
15     int imin,imax;  
16     for(imin=1;imin<=N;imin++)  
17     {  
18         for(imax=imin;imax<=N;imax++)  
19         {  
20             Start=BC(PartSum,imin,imax,M);  
21             All=BC(PartSum,imin,imax,M);  
22             for(j=M-1;j>=1;j--)  
23             {  
24                 if(Start>0)  
25                     Start+=BC(PartSum,imin,imax,j);  
26                 else  
27                     Start=BC(PartSum,imin,imax,j);  
28                 if(Start>All)  
29                     All=Start;  
30             }  
31             if(All>MaxSum)  
32                 MaxSum=All;  
33         }  
34     }  
35     return MaxSum;  
36 }  
37   
38 int BC(int (*PartSum)[N+1],int imin,int imax,int j) //imin--imax第j列的和  
39 {  
40     int value;  
41     value=PartSum[imax][j]-PartSum[imin-1][j]-PartSum[imax][j-1]+PartSum[imin-1][j-1];  
42     return value;  
43 }  

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

扩展问题二:

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

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

 1 int main()
 2 {
 3     int m,n;         
 4     scanf("%d%d",&m,&n);     
 5     int temp[101][101]; 
 6     int result[10000];
 7     int s[202][202];
 8     int i,j,p,q;
 9 
10     memset(temp,0,sizeof(temp));  
11     memset(s,0,sizeof(s));  
12     memset(result,0,sizeof(result)); 
13      //输入二维数组    
14     for( i=1; i<=m; i++)            
15          for( j=1; j<=n; j++)                         
16              {
17                 scanf("%d",&s[i][j]);  
18                 s[i][n+j] = s[i][j];
19                 s[m+i][j] = s[i][j];
20                 s[m+i][n+j] = s[i][j];
21             }
22     int r =0;
23     for( i=0; i<m;i++)
24         for( j=0; j<n;j++)
25             {
26                 for( p=1; p<=m; p++)            
27                     {
28                         for(q=1; q<=n; q++)     
29                             temp[p][q] = s[p+i][q+j];
30                     }
31                       result[r++] = MaxSum2(m,n,temp);   //记录各种情况下的最大子矩阵和
32             }
33   
34       int maxresult = result[0];          //从各种情况下的最大子矩阵和中找出最大的值
35     for (i=1; i<m;i++)
36         {
37             if(maxresult < result[i])
38                 maxresult = result[i];
39         }
40       printf("%d
",maxresult);
41       return 0; 
42 }
43 
44 //用动态规划算法计算“最大子段和问题”,对应于一维数组  
45 int MaxSum(int n,int* a)
46 {        
47        int sum=0,b=0;                
48        for (int i=1;i<=n;i++)     
49        {                  
50            if (b>0) b+=a[i];                             
51           else b=a[i];    
52              if (b>sum)  sum=b;         
53       }         
54 return sum;  
55 }
 1 int MaxSum2(int m, int n, int a[][101])
 2 {
 3     int sum=0;        
 4     int b[101]; 
 5     int i,j,k,p,t1=0,t2=0;
 6     memset(b,0,sizeof(b));         //内存空间初始化   
 7     for ( i=1; i<=m; i++)
 8         {   
 9             int flag_m = m , flag_n = n;
10             for( k=1; k<=2*n; k++)
11             b[k]=0;   
12             for ( j=i; j < flag_m + i; j++) 
13               {   
14                  t1 = j>m? j-m : j;
15                      for( k=1; k <=n  ; k++) 
16                      {
17                         t2 = k>n? k-n : k;
18                         b[k] += a[t1][t2];      
19                      }   
20                        int max = MaxSum(2*n,b);   
21                   if(max>sum) sum = max;   
22                 
23               }         
24 }      
25      return sum;
26 }

有些思路也是从网上借鉴的,看了很多种解法,然后结合自己的解法跟大家分享下。

 

原文地址:https://www.cnblogs.com/huiyuan/p/liantong.html