HDU 5091 Beam Cannon (扫描线思想)

题意:移动一个矩形,使矩形内包含的点尽量多。

思路:把一个点拆成两个事件,一个进(权值为1)一个出(权值为-1),将所有点按照x排序,然后扫描,对于每个x,用一个滑窗计算一下最大值,再移动扫描线。树状数组可以实现。

上面方法其实不是最优的,目前所知最优的办法是把一个矩形压缩成一个点,而一个点延伸为一条线,遇到点的时候更新y+h的一个区间。(线段树懒操作),然后询问线段树上点(矩形)的最值。必须用线段树,时间复杂度会低一些。

类似思路的题目Seoul2007 LA3905,Meteor流星

只写了树状数组版,鉴于扫描线需要进一步学习,待更。

当时做的时候就知道是线段树,可惜我并不会写扫描线,以前尝试写过,挂了,基础有待加强。

#include<cstdio>
#include<algorithm>
using namespace std;

const int maxh = 40000+5;
const int maxn = 10000+5;

int C[maxh];
#define lowbit(x) (x&(-x))
void add(int x,int v)
{
    while(x <= 40001){
        C[x] += v; x += lowbit(x);
    }
}

int sum(int x){
    int res = 0;
    while(x>0){
        res += C[x]; x -= lowbit(x);
    }
    return res;
}
struct Point
{
    int x,y;
    bool operator < (const Point & rhs) const {
        return x < rhs.x;
    }
}poi[maxn];

int main()
{
    //freopen("in.txt","r",stdin);
    int N;
    while(~scanf("%d",&N)&& N>0){
        int W,H;
        scanf("%d%d",&W,&H);
        for(int i = 0; i < N; i++){
            scanf("%d%d",&poi[i].x,&poi[i].y);
            poi[i].y += 20001;
        }
        sort(poi,poi+N);
        int L , R; L = R = 0;
        int ans = 0;
        while(R<N){
            while(poi[R].x - poi[L].x <= W && R<N){
                add(poi[R++].y,1);
            }
            for(int i = 1,sz = 40001 - H ; i <= sz ; i++){
                ans = max(ans,sum(i+H)-sum(i-1));
            }
            if(R<N)
            while(poi[R].x - poi[L].x > W){
                add(poi[L++].y,-1);
            }
        }
        printf("%d
",ans);
        while(L<N){
            add(poi[L++].y,-1);
        }
    }
    return 0;
}

 

原文地址:https://www.cnblogs.com/jerryRey/p/4655941.html