洛谷P1565牛宫

  传送门:题目点这里;

  首先理解题目,就是要求给定矩阵中权值和不小于零的最大子矩阵,数据范围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;
}
蒟蒻写博客不易,如果有误还请大佬们提出
如需转载,请署名作者并附上原文链接,蒟蒻非常感激
名称:HolseLee
博客地址:www.cnblogs.com/cytus
个人邮箱:1073133650@qq.com
原文地址:https://www.cnblogs.com/cytus/p/7763045.html