问题:
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)时间内计算出整体之和)。
- #include <iostream>
- #include <algorithm>
- using namespace std;
- #define MAXN 1003
- int A[MAXN][MAXN];
- long long PS[MAXN][MAXN];
- inline long long MatrixSum(int s, int t, int i, int j)
- {
- return PS[i][j]-PS[i][t-1]-PS[s-1][j]+PS[s-1][t-1];
- }
- int main()
- {
- int m, n, i, j;
- cin >> n >> m;
- for (i=1; i<=n; i++)
- for (j=1; j<=m; j++)
- cin >> A[i][j];
- for (i=0; i<=n; i++)
- PS[i][0] = 0;
- for (j=0; j<=m; j++)
- PS[0][j] = 0;
- // 计算矩阵的部分和
- for (i=1; i<=n; i++)
- for (j=1; j<=m; j++)
- PS[i][j] = A[i][j]+PS[i-1][j]+PS[i][j-1]-PS[i-1][j-1];
- int a, c;
- long long All = A[1][1];
- for (a=1; a<=n; a++)
- for (c=a; c<=n; c++)
- {
- // 将子矩阵上下边界设为第a行和第c行,在这些子矩阵中取最大值
- long long Tail = MatrixSum(a, 1, c, 1);
- for (j=2; j<=m; j++)
- {
- Tail = max(MatrixSum(a, j, c, j),
- MatrixSum(a, j, c, j)+Tail);
- All = max(Tail, All);
- }
- }
- cout << All;
- }
若二维数组的左右首尾也能相连,像一条首尾相连的带子,算法如下:
- #include <iostream>
- #include <algorithm>
- using namespace std;
- #define MAXN 1003
- int A[MAXN][MAXN];
- long long PS[MAXN][MAXN];
- inline long long MatrixSum(int s, int t, int i, int j)
- {
- return PS[i][j]-PS[i][t-1]-PS[s-1][j]+PS[s-1][t-1];
- }
- int main()
- {
- int m, n, i, j;
- cin >> n >> m;
- for (i=1; i<=n; i++)
- for (j=1; j<=m; j++)
- cin >> A[i][j];
- for (i=0; i<=n; i++)
- PS[i][0] = 0;
- for (j=0; j<=m; j++)
- PS[0][j] = 0;
- // 计算矩阵的部分和
- for (i=1; i<=n; i++)
- for (j=1; j<=m; j++)
- PS[i][j] = A[i][j]+PS[i-1][j]+PS[i][j-1]-PS[i-1][j-1];
- int a, c;
- long long All = A[1][1];
- // 上下边界不会跨过第n行和第1行
- for (a=1; a<=n; a++)
- for (c=a; c<=n; c++)
- {
- // 将子矩阵上下边界设为第a行和第c行
- // 左右边界不会跨过第m列和第1列
- long long Tail = MatrixSum(a, 1, c, 1);
- for (j=2; j<=m; j++)
- {
- Tail = max(MatrixSum(a, j, c, j),
- MatrixSum(a, j, c, j)+Tail);
- All = max(Tail, All);
- }
- // 左右边界会跨过第n列和第1列
- long long Sum = MatrixSum(a, 1, c, 1);
- long long Start = Sum;
- int sind = 1;
- for (i=2; i<=m; i++)
- {
- Sum += MatrixSum(a, i, c, i);
- if (Sum > Start) {Start = Sum; sind = i;}
- }
- Tail = MatrixSum(a, m, c, m);
- int tind = m;
- for (j=m-1; j>=1; j--)
- {
- Sum += MatrixSum(a, j, c, j);
- if (Sum > Tail) {Tail = Sum; tind = j;}
- }
- if (sind<tind && Start+Tail>All)
- All = Start+Tail;
- }
- cout << All;
- }
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];
- #include <iostream>
- #include <algorithm>
- using namespace std;
- #define MAXN 1003
- int A[MAXN][MAXN][MAXN];
- int PS[MAXN][MAXN][MAXN];
- inline int CubeSum(int a, int b, int c, int d, int i, int j)
- {
- return PS[b][d][j]-PS[a-1][d][j]-PS[b][c-1][j]-PS[b][d][i-1]+
- 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];
- }
- int main()
- {
- int n, m, h, i, j, k;
- cin >> n >> m >> h;
- for (i=1; i<=n; i++)
- for (j=1; j<=m; j++)
- for (k=1; k<=h; k++)
- cin >> A[i][j][k];
- for (i=0; i<=n; i++)
- for (j=0; j<=m; j++)
- PS[i][j][0] = 0;
- for (i=0; i<=n; i++)
- for (k=0; k<=h; k++)
- PS[i][0][k] = 0;
- for (j=0; j<=m ; j++)
- for (k=0; k<=h; k++)
- PS[0][j][k] = 0;
- // 计算长方体的部分和
- for (i=1; i<=n; i++)
- for (j=1; j<=m; j++)
- for (k=1; k<=h; k++)
- 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];
- int a, b, c, d;
- int All = A[1][1][1];
- // 限制第一维的取值范围
- for (a=1; a<=n; a++)
- for (b=a; b<=n; b++)
- // 限制第二维的取值范围
- for (c=1; c<=m; c++)
- for (d=c; d<=m; d++)
- {
- // 只剩下最后一维没有确定,利用一维部分和的方法
- int Tail = CubeSum(a,b,c,d,1,1);
- for (j=2; j<=k; j++)
- {
- int cur = CubeSum(a,b,c,d,j,j);
- Tail = max(Tail+cur, cur);
- All = max(Tail, All);
- }
- }
- cout << All;
- }