[NOIP2015模拟10.22] 最大子矩阵 解题报告(单调栈)
唉,这道题我一直看的很懵,谈谈理解吧。
首先我们要知道,对于一个矩形2*s,若2*(s-1)是非常酷的,那么判断他们“连接”处的那个2*2矩形是不是酷的,如果是,那么这个2*s矩形就是非常酷的。
拓展开的话,我们需要从a数组中得到b数组,注意,b数组的大小和a数组的大小是不一样的。其实,b数组中的每一个元素对应a数组中的一个2*2矩形。b数组的i,j代表的就是啊数组中从以i,j为右下角的2*2矩形向上可以延伸多长的非常酷的矩形。这样的话我们发现其实b数组中左右相邻的元素放在一起组成的2*3矩形也是非常酷的,原因就是它们之间是有重叠部分的。那么接下来我们只需要对b数组统计最大子矩形就好了,用经典单调栈来维护。值得注意的是,我们要把b数组中没有更新的元素变成0,至于为什么读者只需要脑补第一行和第二行之间的关系就好。
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int maxn=1000+15; int k,s,ass; int a[maxn][maxn],b[maxn][maxn],p[maxn]; int main() { scanf("%d%d",&k,&s); for (int i=1;i<=k;i++) for (int j=1;j<=s;j++) { scanf("%d",&a[i][j]); b[i][j]=1; } for(int i=2;i<=k;i++) for(int j=2;j<=s;j++) if(a[i-1][j-1]-a[i-1][j]<=a[i][j-1]-a[i][j]) b[i][j]=b[i-1][j]+1; for (int i=1;i<=k;i++) for (int j=1;j<=s;j++) if (b[i][j]==1) b[i][j]=0; p[0]=1; for(int i=2;i<=k;i++) { int tot=0; for(int j=2;j<=s;j++) { while(tot&&b[i][j]<=b[i][p[tot]]) ass=max(ass,b[i][p[tot]]*(j-p[--tot])); p[++tot]=j; } while(tot) ass=max(ass,b[i][p[tot]]*(s+1-p[--tot])); } printf("%d\n",ass); return 0; }
星星之火,终将成燎原之势