首页 > 代码库 > 编程之美读书笔记2.15 - 子数组之和的最大值(二维)
编程之美读书笔记2.15 - 子数组之和的最大值(二维)
http://blog.csdn.net/pipisorry/article/details/39083073
问题:
求二维数组(矩阵)的子矩阵之和的最大值。
亦可见:http://poj.org/problem?id=1050
解法1:(解释见注释)
每个子矩阵由列长、行长和左上角的元素位置决定。如果我们指定左上角的元素位置 (i,j) 和列长 c,那么可以求所有这些子矩阵中和最大的。然后,变化列长 c,可以求以 (i,j) 为左上角的最大和子矩阵。最所有左上角位置再求最大和子矩阵,问题就解决了。令 OPT(i,j,c) 表示以 (i,j) 为左上角,列长为 c 的最大和子矩阵之和,OPT(i,j) 表示以 (i,j) 为左上角的最优解,而 S(i,u,v) 表示第 i 行中从列 u 到列 v所有元素之和。则
OPT(i,j,c) = OPT(i+1,j,c) + S(i,j,j+c-1)
OPT(i,j) = max { OPT(i,j,c) : 1 <= c <= n }
其中,j+c-1 <= n。当 i >m 时, OPT(i,j,c) = 0。一共有 O(mn) 个 OPT(i,j) 子问题,而每个 OPT(i,j) 又可以有 n 个决策,因此,总的解规模有 O(mn2 ) 个 OPT(i,j,c)。每个这样的子问题可以在 O(1) 时间内解决(想想怎么做到),因此,时间复杂度为 O(mn2 )。
/* 直接枚举法O( N^2*M^2*O(sum()) ) Time Limit Exceeded */ static int submatrixSum(int **a, int row, int row_end, int col, int col_end){ int sum = 0; for(int i = row; i <= row_end; i++) for(int j = col; j <= col_end; j++) sum += a[i][j]; return sum; } static int maxSubmatrixSum1(int **a, int n){ int max_sum = INT_MIN; int sum; int max_row, max_row_end, max_col, max_col_end; for(int row = 0; row < n; row++){ for(int col = 0; col < n; col++){ //子矩阵左上角位置 for(int row_end = row; row_end < n; row_end++) for(int col_end = col; col_end < n; col_end++){ //4个属性确定一个子矩阵 sum = submatrixSum(a, row, row_end, col, col_end); //计算每个子矩阵的和 if(sum > max_sum){ max_sum = sum; /*max_row = row; max_row_end = row_end; max_col = col; max_col_end = col_end;*/ //printf("%d\n", max_sum); } } } } /*printf("\n col\tcol_end\n %d\t%d\n", max_col, max_col_end); //输出子矩阵位置(4个属性) printf("row %d\nrow_end %d\n", max_row, max_row_end);*/ return max_sum; }
解法2:
/* DP算法 O(N^2*M) */ static int maxSubmatrixSum2(int **a, int n){ int ***row_sum = (int ***)malloc(sizeof(int **) * n); for(int i = 0; i < n; i++) assert( row_sum[i] = (int **)malloc(sizeof(int *) * n) ); for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) assert( row_sum[i][j] = (int *)malloc(sizeof(int) * n) ); //计算row_sum[i][j][c]为第i行j列到c列的和 for(int i = 0; i < n; i++) //初始化row_sum[i][j][c]为0 for(int j = 0; j < n; j++) memset(row_sum[i][j], 0, sizeof(int) * n); //!!! for(int i = n - 1; i >= 0; i--){ for(int j = 0; j < n; j++){ for(int c = j; c < n; c++) if(c == 0) row_sum[i][j][c] = a[i][c]; else row_sum[i][j][c] = row_sum[i][j][c - 1] + a[i][c]; } } //将row_sum[i][j][c]转换成第i行j列到c列的和的最优解 for(int i = n - 2; i >= 0; i--){ //row_sum[n-1][j][c]不变 for(int j = 0; j < n; j++) for(int c = j; c < n; c++){ if(row_sum[i+1][j][c] > 0) row_sum[i][j][c] += row_sum[i+1][j][c]; } } //求以[i, j]为左上角的矩形最优解 int **optij = (int **)malloc(sizeof(int *) * n); for(int i = 0; i < n; i++) optij[i] = (int *)malloc(sizeof(int) * n); for(int i = 0; i < n; i++) memset(optij[i], INT_MIN, sizeof(int) * n); for(int i = 0; i < n; i++){ for(int j = 0; j < n; j++) for(int c = j; c < n; c++){ if(row_sum[i][j][c] > optij[j][j]) optij[j][j] = row_sum[i][j][c]; } } //求整体最优解 int max_sum = INT_MIN; for(int i = 0; i < n; i++){ for(int j = 0; j < n; j++){ if(optij[i][j] > max_sum) max_sum = optij[i][j]; } } return max_sum; }
测试:
int main(){ assert( freopen("BOP\\maxSubmatrixSum.in", "r", stdin) ); //int cases; //测试案例数目 //scanf("%d", &cases); //while(cases--){ int n; //每个案例中matrix维度 scanf("%d", &n); int **a = (int **)malloc(sizeof(int*) * n); for(int i = 0; i < n; i++) a[i] = (int *)malloc(sizeof(int) * n); for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) scanf("%d", &a[i][j]); //printf("%d\n", maxSubmatrixSum1(a, n) ); printf("%d\n", maxSubmatrixSum2(a, n) ); //} fclose(stdin); return 0; }测试案例:
3
2
1 1
1 1
4
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
3
3 -1 4
3 -1 4
3 -1 4
output:
4
15
18
from:
http://blog.csdn.net/pipisorry/article/details/39083073
ref:
http://blog.csdn.net/pipisorry/article/details/39048485
编程之美 书p190
编程之美读书笔记2.15 - 子数组之和的最大值(二维)