最大子矩阵和
1. 简述
给定一个n*n(0<n<=100)的矩阵,请找到此矩阵的一个子矩阵,并且此子矩阵的各个元素的和最大,输出这个最大的值。
Example:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
最大子矩阵为:
9 2
-4 1
-1 8
2. 原理
最大子矩阵和是最大子序列和的二维扩展。
穷举所有子矩阵的话,有C(n,2)*C(n*2)/2个子矩阵,就是n^4个子矩阵,这还不算每个子矩阵求和的时间,就到了O(n^4)。
转化为最大子序列求解的思路是:固定第i列到第j列的范围,寻找在这个范围内的最大子矩阵,这个寻找过程,把每行第i列上的元素到第j列的元素分别求和,就转变为了一维的情况。由于有C(n,2)种列的组合,而求一维的子序列需要n的时间,所以,总体上时间复杂度为O(n^3)。
3. 递推公式
A的下标从0开始,为了增加哨兵,F,P,Q的下标都从1开始。
· A[N][N]表示输入矩阵。
· F[N+1][N+1],F[i][j]表示从第i行的第0列到第j列的元素和。
F[i][j]=0,j=0
F[i][j]=A[i-1][j-1], j=1
F[i][j]=F[i][j-1]+A[i-1][j-1],j>1
这样当固定第i列到第j列的时候,转化为一维数组的第k个元素就可以表示为F[K][j]-F[K][i-1],K是行下标,i和j都是列下标,由于矩阵的统计是从1开始的,所以i,j,k都是大于等于1的,即i-1不会下标越界,这就是前面哨兵的作用。
固定i列和j列的时候的求解递推公式:
· P[N+1],P[k]表示固定第i列到第j列时,从第1行到第k行范围内,包括第k行的最大子矩阵。
· Q[N+1],Q[k]表示固定第i列到第j列时,从第1行到第k行范围内,最大子矩阵。
P[k]=F[0][j]-F[0][i-1], k=1
P[k]=P[k-1]>0?(P[k-1]+F[k][j]-F[k][i-1]):(F[k][j]-F[k][i-1]),k>1
Q[k]=P[k], k=1
Q[k]=max{Q[k-1], P[k]}, k>1
Q[N]即为固定列(i-j)范围内的最大和。
4. 代码
using namespace std;
#define N 4
int main() {
int A[N][N] = { 0, -2, -7, 0, 9, 2, -6, 2, -4, 1, -4, 1, -1, 8, 0, -2 };
int F[N+1][N+1];
int P[N+1];
int Q[N+1];
int max, index_i, index_j;
// F
for(int i=1; i<N+1; i++) {
F[i][0] = 0; // 第0列,即哨兵列
F[i][1] = A[i-1][0]; // 第1列
}
for(int i=1; i<N+1; i++) {
for(int j=2; j<N+1; j++) { // 从第2列开始
F[i][j] = F[i][j-1] + A[i-1][j-1];
}
}
// P,Q
max = INT_MIN;
for(int i=1; i<N+1; i++) {
for(int j=i+1; j<N+1; j++) {
P[1] = F[1][j]-F[1][i-1];
Q[1] = P[1];
for(int k=2; k<N+1; k++) {
P[k] = P[k-1]>0?(P[k-1]+F[k][j]-F[k][i-1]):(F[k][j]-F[k][i-1]);
Q[k] = Q[k-1]>P[k]?Q[k-1]:P[k];
}
if(Q[N] > max) {
max = Q[N];
index_i = i;
index_j = j;
}
}
}
// max
cout << max << endl;
cout << index_i << " , " << index_j << endl;
system("PAUSE");
return 0;
}
输出结果如下:
5. 参考
最大子矩阵问题 http://www.cnblogs.com/fll/archive/2008/05/17/1201543.html