题意:
给你一个n * m的矩阵,让你求出最大子矩阵
分析:
首先,一般会想到二维前缀和,但是即使构造出二维前缀和,如果还只是枚举矩阵的左上和右下顶点的还是会T,需要想办法优化。可以考虑降维。
矩阵压缩:
如果是一维的,那就是求最大子串和,我们想办法转化为求最大子串和,由此我们枚举行,我们把所枚举的行看成是一个串求最大字串和即可。
旋转矩阵
这样做的时间复杂度是 O(mn^2)
,而观察道这个题数据范围是nm <=2e5
,为了让mn^2
的值尽可能的小,我们考虑旋转矩阵,让n(行数)小。
AC代码:
#include <bits/stdc++.h>
using namespace std;
int n, m;
int x;
vector<int> vt[200050];//开vector,数组会爆
vector<int>::iterator it;
int sum[200050];//这个数组就是枚举行后得到的一维串
int dp[200050];//求最大子串和
int ans;
void deal()//动态规划求最长子串和
{
for(int i = 1; i <= m; ++i) dp[i] = 0;
for(int i = 1; i <= m; ++i)
{
dp[i] = max(dp[i], sum[i] + dp[i - 1]);
ans = max(ans, dp[i]);
}
}
int main()
{
scanf("%d%d", &n, &m);
if(n <= m)
{
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
scanf("%d", &x);
vt[i].push_back(x);
}
}
}
else
{
for(int i = 1; i <= n; ++i)
{
for(int j = 1; j <= m; ++j)
{
scanf("%d", &x);
vt[j].push_back(x);
}
}
swap(n, m);
}
for(int i = 1; i <= n; ++i)
{
memset(sum, 0, sizeof(sum));
//枚举的行上界变了,由枚举的行确定的串需要清零
for(int j = i; j <= n; ++j)
//因为枚举的行下界是从上开始向下枚举的,所以只需要在原来的基础上加上当前行的值,即可构造出新的串,不需要清空
{
it = vt[j].begin();
for(int k = 1; k <= m; ++k, ++it) sum[k] += *it;
deal();//处理这个串,即动态规划求最长子串和
}
}
printf("%d\n", ans);
return 0;
}