https://vjudge.net/problem/UVA-10755
题意:
给出一个长方体,叫你求哪个子矩阵的和最大,输出的是最大值。
思路:
首先,n的规模是20,那么最常规的算法,枚举起点,枚举终点,之后循环计算,那么这个的复杂度就是O(N^9)。
这个想法其实是比较自然的,那么可以加一些优化进去,减小复杂度。
首先,三维的问题,可以通过降维解决,枚举竖坐标,降成一维解决。
降成一维之后,如果还是枚举起点与终点,那么复杂度还是高达O(N^8)。
那么其实坐标平面的问题可以用前缀和来优化,所以再用上前缀和优化。
之后,最大子矩阵就可以用动态规划解决了。
tmp = sum[i][j] - sum[k-1][j],这里的sum记录的是降维之后的前缀和,然后k从1到x枚举,i从k到x枚举,j从1到y枚举,
这个操作把一个矩阵变成了一条线,于是就用最大连续和的思想解决。
总的复杂度是O(N^5)。
代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 using namespace std; 5 6 long long a[25][25][25]; 7 8 long long b[25][25]; 9 long long sum[25][25]; 10 11 int main() 12 { 13 int t; 14 15 scanf("%d",&t); 16 17 while (t--) 18 { 19 long long ans = -1e18; 20 21 int x,y,z; 22 23 scanf("%d%d%d",&x,&y,&z); 24 25 for (int i = 1;i <= x;i++) 26 for (int j = 1;j <= y;j++) 27 for (int k = 1;k <= z;k++) 28 scanf("%lld",&a[i][j][k]); 29 30 for (int i = 1;i <= z;i++) 31 for (int j = i;j <= z;j++) 32 { 33 memset(b,0,sizeof(b)); 34 35 for (int k = i;k <= j;k++) 36 { 37 for (int m = 1;m <= x;m++) 38 for (int n = 1;n <= y;n++) 39 b[m][n] += a[m][n][k]; 40 } 41 42 memset(sum,0,sizeof(sum)); 43 44 for (int m = 1;m <= x;m++) 45 for (int n = 1;n <= y;n++) 46 { 47 sum[m][n] = sum[m-1][n] + sum[m][n-1] - sum[m-1][n-1] + b[m][n]; 48 } 49 50 for (int m = 1;m <= x;m++) 51 for (int n = m;n <= x;n++) 52 { 53 long long orz = 0; 54 55 for (int k = 1;k <= y;k++) 56 { 57 long long tmp = sum[n][k] - sum[m-1][k]; 58 59 ans = max(ans,tmp - orz); 60 61 orz = min(tmp,orz); 62 } 63 } 64 } 65 66 printf("%lld ",ans); 67 68 if (t) printf(" "); 69 } 70 71 return 0; 72 }