bzoj3885: [Usaco2015 Jan]Cow Rectangles【悬线法+二分】

本文介绍了一种使用悬线法结合二分查找解决坐标系中寻找包含最多'H'字符且不含'G'字符的子矩形问题的方法。通过预处理和单调栈实现了高效求解,最终输出'H'的最大数量及对应子矩形的最小面积。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

坐标系上给出n个点,分”H”和”G”,一个整点坐标上至多一个点。 现在求一个不包含”G”的包含尽量多”H”的子矩形,然后在保证”H”最多的情况下还要问最小面积。 输出”H”的最大数量,和保证”H”最多时的最小矩形面积,坐标范围在[0,1000]。

解题思路:

先用悬线法求出极大子矩阵,再二分边界,消去不含’H’的部分即可。
悬线法:对每个位置求出向上的没有障碍点的最大距离,再用单调栈求出左右能拓展的最远边界即是一个最大子矩阵,时间复杂度O(n2)O(n2),而且在一些题目中可以离散化.

#include<bits/stdc++.h>
using namespace std;

const int N=1005;
int n,top,ans1,ans2,a[N][N],f[N][N],stk[N],l[N],r[N],h[N];

int get_num(int L,int R,int U,int D)
{
    return f[D][R]-f[D][L-1]-f[U-1][R]+f[U-1][L-1];
}

int get_area(int L,int R,int U,int D)
{
    int fl,fr,fu,ll,rr;
    ll=L,rr=R;
    while(ll<=rr)
    {
        int mid=ll+rr>>1;
        if(!get_num(L,mid,U,D))ll=mid+1;
        else rr=mid-1;
    }fl=ll;
    ll=L,rr=R;
    while(ll<=rr)
    {
        int mid=ll+rr>>1;
        if(!get_num(mid,R,U,D))rr=mid-1;
        else ll=mid+1;
    }fr=rr;
    ll=U,rr=D;
    while(ll<=rr)
    {
        int mid=ll+rr>>1;
        if(!get_num(L,R,U,mid))ll=mid+1;
        else rr=mid-1;
    }fu=ll;
    return (fr-fl)*(D-fu);
}

int main()
{
    //freopen("lx.in","r",stdin);
    scanf("%d",&n);int x,y;char s[5];
    while(n--)scanf("%d%d%s",&x,&y,s),a[++x][++y]=(s[0]=='H'?1:2);
    n=1001;
    for(int i=1;i<=n;i++)
    {
        int tmp=0;
        for(int j=1;j<=n;j++)
            f[i][j]=f[i-1][j]+(a[i][j]==1?++tmp:tmp);
    }
    h[0]=h[n+1]=-1;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)h[j]=(a[i][j]<2?h[j]+1:0);
        top=0;
        for(int j=1;j<=n+1;j++)
        {
            while(h[j]<h[stk[top]])r[stk[top--]]=j;
            stk[++top]=j;
        }
        top=0;
        for(int j=n;j>=0;j--)
        {
            while(h[j]<h[stk[top]])l[stk[top--]]=j;
            stk[++top]=j;
        }
        for(int j=1;j<=n;j++)
            if(h[j])
            {
                int tmp=get_num(l[j]+1,r[j]-1,i-h[j]+1,i);
                if(tmp>ans1)ans1=tmp,ans2=get_area(l[j]+1,r[j]-1,i-h[j]+1,i);
                else if(tmp==ans1)ans2=min(ans2,get_area(l[j]+1,r[j]-1,i-h[j]+1,i));
            }
    }
    cout<<ans1<<'\n'<<ans2<<'\n';
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值