编程之美2.15——二维子数组之和的最大值

问题:

1. 求二维数组(矩阵)的子矩阵之和的最大值。

2. 求三维数组(长方体)的子方体之和的最大值。


解法:

先计算出以左上角的元素(1,1)和当前元素(i,j)为顶点对的子矩阵的部分和,部分和的计算如下

PS[i][j] = A[i][j]+PS[i-1][j]+PS[i][j-1]-PS[i-1][j-1]

上一篇文章中我们发现一维的解答可以线性完成,这里我们把问题从二维转化为一维以提高算法性能。

假设已经确定了矩阵区域的上下边界,不如知道矩阵区域的上下边界分布是第a行和第c行,接下来要确定左右边界。

我们把第a行和第c行之间的每一列看成一个整体,相当于一维数组中的一个元素(通过子矩阵部分和可以在O(1)时间内计算出整体之和)。

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <algorithm>  
  3. using namespace std;  
  4.   
  5. #define MAXN 1003  
  6. int A[MAXN][MAXN];  
  7. long long PS[MAXN][MAXN];  
  8.   
  9. inline long long MatrixSum(int s, int t, int i, int j)  
  10. {  
  11.     return PS[i][j]-PS[i][t-1]-PS[s-1][j]+PS[s-1][t-1];  
  12. }  
  13.   
  14. int main()  
  15. {  
  16.     int m, n, i, j;  
  17.     cin >> n >> m;  
  18.     for (i=1; i<=n; i++)  
  19.         for (j=1; j<=m; j++)  
  20.             cin >> A[i][j];  
  21.     for (i=0; i<=n; i++)  
  22.         PS[i][0] = 0;  
  23.     for (j=0; j<=m; j++)  
  24.         PS[0][j] = 0;  
  25.     // 计算矩阵的部分和  
  26.     for (i=1; i<=n; i++)  
  27.         for (j=1; j<=m; j++)  
  28.             PS[i][j] = A[i][j]+PS[i-1][j]+PS[i][j-1]-PS[i-1][j-1];  
  29.     int a, c;  
  30.     long long All = A[1][1];  
  31.     for (a=1; a<=n; a++)  
  32.         for (c=a; c<=n; c++)  
  33.         {  
  34.             // 将子矩阵上下边界设为第a行和第c行,在这些子矩阵中取最大值  
  35.             long long Tail = MatrixSum(a, 1, c, 1);  
  36.             for (j=2; j<=m; j++)  
  37.             {  
  38.                 Tail = max(MatrixSum(a, j, c, j),   
  39.                     MatrixSum(a, j, c, j)+Tail);   
  40.                 All = max(Tail, All);  
  41.             }  
  42.         }  
  43.     cout << All;  
  44. }  

若二维数组的左右首尾也能相连,像一条首尾相连的带子,算法如下:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <algorithm>  
  3. using namespace std;  
  4.   
  5. #define MAXN 1003  
  6. int A[MAXN][MAXN];  
  7. long long PS[MAXN][MAXN];  
  8.   
  9. inline long long MatrixSum(int s, int t, int i, int j)  
  10. {  
  11.     return PS[i][j]-PS[i][t-1]-PS[s-1][j]+PS[s-1][t-1];  
  12. }  
  13.   
  14. int main()  
  15. {  
  16.     int m, n, i, j;  
  17.     cin >> n >> m;  
  18.     for (i=1; i<=n; i++)  
  19.         for (j=1; j<=m; j++)  
  20.             cin >> A[i][j];  
  21.     for (i=0; i<=n; i++)  
  22.         PS[i][0] = 0;  
  23.     for (j=0; j<=m; j++)  
  24.         PS[0][j] = 0;  
  25.     // 计算矩阵的部分和  
  26.     for (i=1; i<=n; i++)  
  27.         for (j=1; j<=m; j++)  
  28.             PS[i][j] = A[i][j]+PS[i-1][j]+PS[i][j-1]-PS[i-1][j-1];  
  29.     int a, c;  
  30.     long long All = A[1][1];  
  31.     // 上下边界不会跨过第n行和第1行  
  32.     for (a=1; a<=n; a++)  
  33.         for (c=a; c<=n; c++)  
  34.         {     
  35.             // 将子矩阵上下边界设为第a行和第c行  
  36.             // 左右边界不会跨过第m列和第1列  
  37.             long long Tail = MatrixSum(a, 1, c, 1);  
  38.             for (j=2; j<=m; j++)  
  39.             {  
  40.                 Tail = max(MatrixSum(a, j, c, j),   
  41.                     MatrixSum(a, j, c, j)+Tail);   
  42.                 All = max(Tail, All);  
  43.             }  
  44.             // 左右边界会跨过第n列和第1列  
  45.             long long Sum = MatrixSum(a, 1, c, 1);  
  46.             long long Start = Sum;  
  47.             int sind = 1;  
  48.             for (i=2; i<=m; i++)  
  49.             {  
  50.                 Sum += MatrixSum(a, i, c, i);  
  51.                 if (Sum > Start) {Start = Sum; sind = i;}  
  52.             }  
  53.             Tail = MatrixSum(a, m, c, m);  
  54.             int tind = m;  
  55.             for (j=m-1; j>=1; j--)  
  56.             {  
  57.                 Sum += MatrixSum(a, j, c, j);  
  58.                 if (Sum > Tail) {Tail = Sum; tind = j;}  
  59.             }  
  60.             if (sind<tind && Start+Tail>All)  
  61.                 All = Start+Tail;  
  62.         }  
  63.     cout << All;  
  64. }  


2. 解法:

思路和二维一样,但时间复杂度增加到O(n^5)。通过将高维转化为低维的方法,每增加一维时间复杂度要增加O(n^2)。
方体的部分和计算公式如下:PS[i][j][k] = A[i][j][k]+PS[i-1][j][k]+PS[i][j-1][k]+PS[i][j][k-1]-PS[i-1][j-1][k]-PS[i-1][j][k-1]-PS[i][j-1][k-1]+PS[i-1][j-1][k-1];

[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <algorithm>  
  3. using namespace std;  
  4.   
  5. #define MAXN 1003  
  6. int A[MAXN][MAXN][MAXN];  
  7. int PS[MAXN][MAXN][MAXN];  
  8.   
  9. inline int CubeSum(int a, int b, int c, int d, int i, int j)  
  10. {  
  11.     return PS[b][d][j]-PS[a-1][d][j]-PS[b][c-1][j]-PS[b][d][i-1]+  
  12.         PS[a-1][c-1][j]+PS[a-1][d][i-1]+PS[b][c-1][i-1]-PS[a-1][c-1][i-1];  
  13. }  
  14.   
  15. int main()  
  16. {  
  17.     int n, m, h, i, j, k;  
  18.     cin >> n >> m >> h;  
  19.     for (i=1; i<=n; i++)  
  20.         for (j=1; j<=m; j++)  
  21.             for (k=1; k<=h; k++)  
  22.                 cin >> A[i][j][k];  
  23.     for (i=0; i<=n; i++)  
  24.         for (j=0; j<=m; j++)  
  25.             PS[i][j][0] = 0;  
  26.     for (i=0; i<=n; i++)  
  27.         for (k=0; k<=h; k++)  
  28.             PS[i][0][k] = 0;  
  29.     for (j=0; j<=m ; j++)  
  30.         for (k=0; k<=h; k++)  
  31.             PS[0][j][k] = 0;  
  32.     // 计算长方体的部分和  
  33.     for (i=1; i<=n; i++)  
  34.     for (j=1; j<=m; j++)  
  35.     for (k=1; k<=h; k++)  
  36.         PS[i][j][k] = A[i][j][k]+PS[i-1][j][k]+PS[i][j-1][k]+PS[i][j][k-1]-  
  37.             PS[i-1][j-1][k]-PS[i-1][j][k-1]-PS[i][j-1][k-1]+PS[i-1][j-1][k-1];  
  38.     int a, b, c, d;  
  39.     int All = A[1][1][1];  
  40.     // 限制第一维的取值范围  
  41.     for (a=1; a<=n; a++)  
  42.     for (b=a; b<=n; b++)  
  43.         // 限制第二维的取值范围  
  44.         for (c=1; c<=m; c++)  
  45.         for (d=c; d<=m; d++)  
  46.         {  
  47.             // 只剩下最后一维没有确定,利用一维部分和的方法  
  48.             int Tail = CubeSum(a,b,c,d,1,1);  
  49.             for (j=2; j<=k; j++)  
  50.             {  
  51.                 int cur = CubeSum(a,b,c,d,j,j);  
  52.                 Tail = max(Tail+cur, cur);  
  53.                 All = max(Tail, All);  
  54.             }  
  55.         }  
  56.     cout << All;  
  57. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值