bzoj 1127 KUP —— 最大子矩形+答案构造

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1127

首先,把权值 > 2*k 的点作为“坏点”,然后在图中用悬线法找权值最大的子矩形;

如果权值最大的子矩形的权值 < k ,那么无解;

否则,针对这个子矩形,一列一列地删掉元素,某一时刻权值一定会变成 k~2*k 或 < k;

如果变成 k~2*k ,直接输出即可;

如果变成 < k,那么刚才删掉的那一列的权值 > k;

针对那一列,如果权值和就是 k~2*k,输出那一列;

否则,针对这一列,一个一个删除元素,因为此时元素的权值都是 < k 的,所以总会有某一时刻删成 k~2*k,即为答案;

注意输出的坐标是 列-行 !!!

还要注意一个一个删除列上的元素时,符合答案后的坐标是 i+1 !!!

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int const xn=2005;
int n,k,v[xn][xn],l[xn][xn],r[xn][xn],s[xn][xn];
ll sum[xn][xn];
int rd()
{
  int ret=0,f=1; char ch=getchar();
  while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
  while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
  return f?ret:-ret;
}
ll get(int a,int b,int c,int d){return sum[c][d]-sum[a-1][d]-sum[c][b-1]+sum[a-1][b-1];}
int main()
{
  k=rd(); n=rd(); int a=0,b=0,c,d;
  for(int i=1;i<=n;i++)
    {
      int tmp=1;
      for(int j=1;j<=n;j++)
        {
          v[i][j]=rd();
          sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+v[i][j];
          if(v[i][j]>=k&&v[i][j]<=2*k)a=i,b=j;
          if(v[i][j]>2*k){tmp=j+1; continue;}
          s[i][j]=s[i-1][j]+1;
          if(v[i-1][j]>2*k||i==1)l[i][j]=tmp;
          else l[i][j]=max(tmp,l[i-1][j]);
        }
      tmp=n;
      for(int j=n;j;j--)
        {
          if(v[i][j]>2*k){tmp=j-1; continue;}
          if(v[i-1][j]>2*k||i==1)r[i][j]=tmp;
          else r[i][j]=min(tmp,r[i-1][j]);
        }
    }
  if(a){printf("%d %d %d %d\n",b,a,b,a); return 0;}
  ll mxs=0;
  for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
      {
        if(v[i][j]>2*k)continue;
        int ta=i-s[i][j]+1,tb=l[i][j],tc=i,td=r[i][j];
        ll ts=get(ta,tb,tc,td);
        if(ts<k)continue;
        if(ts>mxs)mxs=ts,a=ta,b=tb,c=tc,d=td;
      }
  if(!mxs){printf("NIE\n"); return 0;}
  if(mxs>=k&&mxs<=2*k){printf("%d %d %d %d\n",b,a,d,c); return 0;}
  for(int j=b;j<=d;j++)
    {
      mxs-=get(a,j,c,j);
      if(mxs>=k&&mxs<=2*k){printf("%d %d %d %d\n",j+1,a,d,c); return 0;}
      else if(mxs<k)
        {
          ll ts=get(a,j,c,j);
          if(ts>=k&&ts<=2*k){printf("%d %d %d %d\n",j,a,j,c); return 0;}
          for(int i=a;i<=c;i++)
            {
              ts-=v[i][j];
              if(ts>=k&&ts<=2*k){printf("%d %d %d %d\n",j,i+1,j,c); return 0;}//i+1!!!
            }
        }
    }
  return 0;
}

 

转载于:https://www.cnblogs.com/Zinn/p/9755343.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值