zoukankan      html  css  js  c++  java
  • 【神一样的作业】二维数组连续的二维子数组的和(元素可以连续)

    结对编程训练

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

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

     

    扩展问题一:

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

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

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

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

     

  • 相关阅读:
    HashMap源码学习
    java线程池
    MySQL的MVCC
    volatile关键字学习
    ArrayList, Vector和CopyOnWriteArrayList对比学习
    曹工说Redis源码(3)-- redis server 启动过程完整解析(中)
    曹工说Redis源码(2)-- redis server 启动过程解析及简单c语言基础知识补充
    曹工杂谈:我们的应用,启动就要去其他服务拉数据,那其他服务挂了,我们就起不来了?
    程序员正确的提问方式(个人建议)
    曹工说Redis源码(1)-- redis debug环境搭建,使用clion,达到和调试java一样的效果
  • 原文地址:https://www.cnblogs.com/huiyuan/p/liantong.html
Copyright © 2011-2022 走看看