传送门:题目点这里;
首先理解题目,就是要求给定矩阵中权值和不小于零的最大子矩阵,数据范围200也还不算棘手,暴力n^4的算法也可以水到50分。正解要用到单调栈配合二分和前缀和,复杂度n^3logn,跑得也还算快。
分析一下,首先用一个数组a[ i ][ j ]记录下第 i 行前 j 个元素之和,然后开始一个个枚举从左边界 i 和右边界 j ,用一个tot变量记录 i 到 j 的元素和,再一行行累加,如果遇到元素和小于零的情况就开始二分,找到一个行号k使得从第k行到该行的元素和大于零,枚举过程中比较得出ans就可以了,下面是代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<iostream> #define ll long long using namespace std; ll n,m,a[202][202],ans; ll sta[202],f[202],top; void ready() { scanf("%lld%lld",&n,&m); for(ll i=1;i<=n;i++){ for(ll j=1;j<=m;j++){ ll x;scanf("%lld",&x); a[i][j]=a[i][j-1]+x;//前缀和,不解释; } } } ll getnum(ll u) { ll l=1,r=top,ret=-1;//二分,从1枚举到当前栈顶,如果找不到就返回0; while(l<=r){ ll mid=(l+r)>>1; if(sta[mid]<u) r=mid-1,ret=mid; else l=mid+1; } return ret; } void work() { for(ll i=1;i<=m;i++){//枚举左边界; for(ll j=1;j<=m;j++){//枚举右边界; ll tot=0;sta[0]=1e10;top=0; for(ll k=1;k<=n;k++){//枚举行数; tot+=(a[k][j]-a[k][i-1]); if(tot>=0)ans=max(ans,(j-i+1)*k);//大于零,直接比较; else{ ll wwy=getnum(tot);//小于零,开始二分; if(wwy!=-1)ans=max(ans,(j-i+1)*(k-f[wwy])); } if(sta[top]>tot)sta[++top]=tot,f[top]=k;//单调栈; } } } printf("%lld",ans); } int main() { //freopen("long.in","r",stdin); //freopen("long.out","w",stdout); ready();work();return 0; }