【小结】最大子矩阵/子段/01子矩阵问题
无言以对
ARC081F
luoguP1169 棋盘制作
小结:
最大子段和问题: (一段序列选一段最大) 直接贪心考虑选还是不选,f[i]表示以I结尾的最大子段和,f[i]=max(f[i-1]+a[i],a[i]).(显然当f[i-1]<0时不选)
最大子矩阵问题: 求一个不固定数值的子矩阵,那么枚举上下横边n^2,然后如果询问数值总和最大,将这一竖列sum当做一个数字,然后一个最大子段和,如果询问面积,单调栈搞。
最大全1子矩阵 将原0/1数列转化为-->这一列的最长连续的1长度,
例如
0 0 1
0 1 1
0 1 1 就转化为了
0 0 1
0 1 2
0 2 3 然后这样我们O(n)枚举每一行,然后用长度最长*f[i][j]来更新答案就可以了,具体实现-->单调栈。
玉蟾宫bzoj3039(权限)code(01最大子矩阵板题):
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> using namespace std; const int maxn = 1005; int n,m; char ss[maxn][maxn]; int f[maxn][maxn]; int ans; int o[maxn]; int sta[maxn],top; int l[maxn],r[maxn]; void solve() { top = 0; sta[0] = 0; //维护一个自底往上单增单调栈,这样就可以找到最靠左并且比自己小的位置 for(int i=1;i<=m;i++) { while(top&&o[sta[top]]>=o[i]) top--; l[i] = sta[top] + 1; sta[++top] = i; } top = 0; sta[0] = m+1; for(int i=m;i;i--) { while(top&&o[sta[top]]>=o[i]) top--; r[i] = sta[top] - 1; sta[++top] = i; } for(int i=1;i<=m;i++) { ans = max(ans,o[i]*(r[i]-l[i]+1)); } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { scanf("%s",&ss[i][j]); } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(ss[i][j]=='F') { f[i][j] = f[i-1][j] + 1; } } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) o[j] = f[i][j]; solve(); } printf("%d",ans*3); }