• 洛谷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
  • 相关阅读:
    谷歌三架马车之 The Google File System 中文版
    数据集市 Data Mart
    VMware虚拟机ubuntu下安装VMware Tools步骤
    NOIP普及组 海港 题解
    HXD的DS
    离散化
    哈希表
    状态压缩DP 初探
    《信息学奥赛一本通》大盗阿福 题解
    NOIP 加工零件 题解
  • 原文地址:https://www.cnblogs.com/cytus/p/7763045.html
Copyright © 2020-2023  润新知