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 }

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

     

  • 相关阅读:
    NOIP2011 D1T1 铺地毯
    NOIP2013 D1T3 货车运输 倍增LCA OR 并查集按秩合并
    POJ 2513 trie树+并查集判断无向图的欧拉路
    599. Minimum Index Sum of Two Lists
    594. Longest Harmonious Subsequence
    575. Distribute Candies
    554. Brick Wall
    535. Encode and Decode TinyURL(rand and srand)
    525. Contiguous Array
    500. Keyboard Row
  • 原文地址:https://www.cnblogs.com/huiyuan/p/liantong.html
Copyright © 2011-2022 走看看