题目:题目链接
题意:给出n*n的矩阵,求和最大的子矩阵。
分析:首先考虑一维的最大子段和问题,给出一个序列a[0],a[1],a[2]...a[n],求出连续的一段,使其总和最大。
a[i]表示第i个元素dp[i]表示以a[i]结尾的最大子段和dp[i] = max{a[i], dp[i-1] + a[i]}
考虑二维的最大子矩阵问题我们可以利用矩阵压缩把二维的问题转化为一维的最大子段和问题。因为是矩阵和,所以我
们可以把这个矩形的高压缩成1,用加法就行了。
也就是对给你的n*n的数组进行逐行压缩,求解每一次压缩之后形成的新的sum数组中最大子段和的最大值,就是题目
的结果。例如题目的数据,首先求把第一行数据0 -2 -7 0放入sum数组中,求sum数组的最大子段和,存入smax中,
然后把sum数组中的数据和第二行数据相加放入sum数组中,再对sum数组求最大子段和,如果比smax大,则覆盖掉
smax,然后依次类推再加下面几行的数据,然后再从第二行重新进行数据的压缩。
代码:
#include <iostream> #include <cstdio> #include <string> #include <string.h> #include <map> #include <vector> #include <cstdlib> #include <algorithm> #include <cmath> #include <queue> #include <set> #include <stack> #include <functional> #include <fstream> #include <sstream> #include <iomanip> #include <numeric> #include <cassert> #include <bitset> #include <stack> #include <ctime> #include <list> #define INF 0x7fffffff #define max3(a,b,c) (max(a,b)>c?max(a,b):c) #define min3(a,b,c) (min(a,b)<c?min(a,b):c) #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; #define maxn 110 int QuickMod(int a,int b,int n) { int r = 1; while(b) { if(b&1) r = (r*a)%n; a = (a*a)%n; b >>= 1; } return r; } int num[maxn][maxn]; int cal(int n, int a[])//最大子段和 { int MAX = -INF; int sum = 0; for(int i = 0; i < n; ++i) { if(sum > 0) sum += a[i]; else sum = a[i]; if(sum > MAX) MAX = sum; } return MAX; } int work(int m, int n) { int MAX = -INF; int tmp[maxn]; for(int i = 0 ; i < m; ++i)//起始行 { for(int k = 0; k < n; ++k) tmp[k] = 0; for(int j = i; j < m; ++j)//从第i行加到最后一行 { int k; for(k = 0; k < n; ++k)//每次把第j行的数都加起来 tmp[k] += num[j][k]; int sum = cal(k, tmp); if(sum > MAX) MAX = sum; } } return MAX; } int main() { int n; scanf("%d", &n); for(int i = 0; i < n; ++i) for(int j = 0; j < n; ++j) scanf("%d", &num[i][j]); int ans = work(n, n); printf("%d\n", ans); return 0; }