標籤:編程之美
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]為0for(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];elserow_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 - 子數組之和的最大值(二維)