码迷,mamicode.com
首页 > 其他好文 > 详细

洛谷P1565牛宫

时间:2017-10-31 20:16:55      阅读:134      评论:0      收藏:0      [点我收藏+]

标签:style   代码   scanf   mes   复杂度   col   cst   for   can   

  传送门:题目点这里;

  首先理解题目,就是要求给定矩阵中权值和不小于零的最大子矩阵,数据范围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;
}

洛谷P1565牛宫

标签:style   代码   scanf   mes   复杂度   col   cst   for   can   

原文地址:http://www.cnblogs.com/cytus/p/7763045.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!