您的位置:首页 > 其它

POJ 2482 Stars in Your Window(离散化+线段树:扫描线)

2014-03-31 15:26 411 查看
POJ 2482 Stars in Your Window(离散化+线段树:扫描线)
http://poj.org/problem?id=2482
题意:有很多点在二维平面内,每个点有一个价值,给你一个平行坐标轴的矩形,只可以移动矩形,问矩形最多能包括多少价值和的点.
分析:
本题首先要转换一下,我们用矩形的中心点来描述这个矩形,然后对于每个星星,我们建立一个矩形中心的活动范围,即矩形中心在该范围内活动就可以覆盖到该星星.其实这个新矩形与原来的矩形长和宽都是相同的.所以我们要求的问题就变成了:任意一个区域(肯定也是矩形的)最多能被矩形覆盖的最大值.(即假如有价值为5和价值为3的矩形覆盖了一个区域,但是不仅仅是边界相交,,那么这片区域的价值为8).
另外题目中要求矩形的边界如果包括了星星就不算.现在转化为新矩形来看这个问题(本题目中我们用x轴作为扫描线的高度,y轴离散化为区域):
如果两个新矩形在平行于x轴的边上重合了,那么可以肯定这边不能算是包括了这两个星星.我们用线段树离散化y轴坐标的时候自然就可以避过这个问题,因为线段树上的每个叶节点表示的是一个半闭半开的区间[y1,y2),[y2,y3) 等.所以如果出现上面那张情况,线段树中的节点不可能会有被两个扫描线覆盖的.
如果两个矩形在平行于y轴的边上重合了一条边,由于此时x轴做扫描线高度,也就是说有两条扫描线重合了,其中一条是+v的下位边,另一条是-v的上位边.这时我们应该先-v在+v. 即我们要先处理上位边,再处理下位边.(否则将出错,仔细想想为什么).这个只需要我们在对所有扫描线排序的时候做点小处理即可.
关于线段树节点维护的信息(采用线保留型线段树):
cnt:保存的是当前节点被覆盖的值.cnt信息不上传也不下传,属于线保留型.
sum:表示该节点控制的区域内,被覆盖的最大值.
由于初始cnt和sum都为0,所以可以用memset代替build
PushUp操作和update操作是必须的.
过程中出了点错误, 由于空间只申请MAXN=10005,导致出错,因为点做多1W个,所以不同的Y坐标有2W个,所以线段树的节点应该是8W.下面的代码同样适合处理浮点数大小的矩阵和点.稍微修改下即可.
AC代码:141ms

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define lson i*2,l,m
#define rson i*2+1,m+1,r
const int MAXN=20000+5;//必须为2W,因为点有1W个,所以扫描线2W个,不同的Y坐标最多有2W个
int cnt[MAXN*4],sum[MAXN*4];
double Y[MAXN];
struct seg
{
    double l,r,h;
    int d;
    seg(){}
    seg(double a,double b,double c,int d):l(a),r(b),h(c),d(d){}
    bool operator <(const seg&b)const
    {
        if(h == b.h) return d<b.d;
        return h<b.h;
    }
}ss[MAXN];
void PushUp(int i)
{
    sum[i]=max(sum[i*2],sum[i*2+1]) + cnt[i];
}
void update(int ql,int qr,int v,int i,int l,int r)
{
    if(ql<=l && r<=qr)
    {
        cnt[i]+=v;
        sum[i]+=v;
        return ;
    }
    int m=(l+r)>>1;
    if(ql<=m) update(ql,qr,v,lson);
    if(m<qr) update(ql,qr,v,rson);
    PushUp(i);
}
int main()
{
    int n;
    double w,h;
    while(scanf("%d%lf%lf",&n,&w,&h)==3)
    {
        if(n==0)
        {
            printf("0\n");
            continue;
        }
        double x,y;
        int val;
        int cnt_y=0,cnt_ss=0;//记录有多少个Y值和扫描线
        for(int i=1;i<=n;i++)
        {
            scanf("%lf%lf%d",&x,&y,&val);
            ss[cnt_ss++] = seg(y-h/2,y+h/2,x-w/2,val);
            ss[cnt_ss++] = seg(y-h/2,y+h/2,x+w/2,-val);
            Y[cnt_y++] = y-h/2;
            Y[cnt_y++] = y+h/2;
        }
        sort(ss,ss+cnt_ss);
        sort(Y,Y+cnt_y);
        cnt_y = unique(Y,Y+cnt_y)-Y;
        int ans=0;
        memset(cnt,0,sizeof(cnt));
        memset(sum,0,sizeof(sum));
        for(int i=0;i<cnt_ss-1;i++)
        {
            int ql=lower_bound(Y,Y+cnt_y,ss[i].l)-Y;
            int qr=lower_bound(Y,Y+cnt_y,ss[i].r)-Y-1;
            if(ql<=qr) update(ql,qr,ss[i].d,1,0,cnt_y-1);
            ans=max(ans,sum[1]);
        }
        printf("%d\n",ans);
    }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: