题目大意:给一个矩阵,找出一个元素之和最大的子矩阵。
可以暴力枚举,不过是时间复杂度是O(n6),通过利用一个sum数组保存已计算的和可以提高效率(sum[i][j]保存从矩阵左上角到m[i][j]的所有元素和),把时间复杂度降为O(n4),都说这是DP,可是我怎么感觉不出来?对DP理解不够深刻的原因吗?
1 #include <cstdio> 2 #define MAXN 105 3 4 int a[MAXN][MAXN], sum[MAXN][MAXN]; 5 6 int main() 7 { 8 #ifdef LOCAL 9 freopen("in", "r", stdin); 10 #endif 11 int n; 12 while (scanf("%d", &n) != EOF) 13 { 14 for (int i = 1; i <= n; i++) 15 for (int j = 1; j <= n; j++) 16 scanf("%d", &a[i][j]); 17 for (int i = 0; i <= n; i++) 18 sum[i][0] = sum[0][i] = 0; 19 for (int i = 1; i <= n; i++) 20 for (int j = 1; j <= n; j++) 21 sum[i][j] = sum[i][j-1] + sum[i-1][j] - sum[i-1][j-1] + a[i][j]; 22 int max = a[1][1]; 23 for (int i = 1; i <= n; i++) 24 for (int p = i; p <= n; p++) 25 for (int j = 1; j <= n; j++) 26 for (int q = j; q <= n; q++) 27 { 28 int t = sum[p][q] - sum[p][j-1] - sum[i-1][q] + sum[i-1][j-1]; 29 if (t > max) max = t; 30 } 31 printf("%d ", max); 32 } 33 return 0; 34 }
还可以进行进一步的优化,将时间复杂度降到O(n3),主要思想就是将二维转化为一维。先枚举行,比如把子矩阵行范围限制在i行到p行之间,然后求这个行范围内每列的和,这样就转化成了一维的最大连续和问题。
1 #include <cstdio> 2 #include <algorithm> 3 #include <climits> 4 using namespace std; 5 #define MAXN 105 6 7 int a[MAXN][MAXN], sum[MAXN][MAXN]; 8 9 int column_sum(int i, int p, int j) 10 { 11 return sum[p][j] - sum[p][j-1] - sum[i-1][j] + sum[i-1][j-1]; 12 } 13 14 int main() 15 { 16 #ifdef LOCAL 17 freopen("in", "r", stdin); 18 #endif 19 int n; 20 while (scanf("%d", &n) != EOF) 21 { 22 for (int i = 0; i <= n; i++) 23 sum[i][0] = sum[0][i] = 0; 24 for (int i = 1; i <= n; i++) 25 for (int j = 1; j <= n; j++) 26 { 27 scanf("%d", &a[i][j]); 28 sum[i][j] = sum[i][j-1] + sum[i-1][j] - sum[i-1][j-1] + a[i][j]; 29 } 30 int gMax = a[1][1]; 31 for (int i = 1; i <= n; i++) 32 for (int p = i; p <= n; p++) 33 { 34 int sum = 0, minSum = 0, lMax = INT_MIN; 35 for (int j = 1; j < n; j++) 36 { 37 sum += column_sum(i, p, j); 38 lMax = max(lMax, sum - minSum); 39 minSum = min(minSum, sum); 40 } 41 if (lMax > gMax) gMax = lMax; 42 } 43 printf("%d ", gMax); 44 } 45 return 0; 46 }