la3905 Meteor 流星 事件触发

这个题目也是以前看过的 当时没有敲的勇气。今天看懂了。
题目实在是太巧妙了啊。。acm的真题果然不一般。



在一个平面内有一个矩形

然后给出n个流动的流星的初始位置和速度向量
求出最多同时多少个流星在这个矩形内
还有 这个矩形边界上的流星不算
矩形左下角是0.0 右上角是 w h
每个流星 四个int 初始位置 x y 和移动向量 a b


数据范围是 w h n, 1-10w  初始位置 正负20w 
a 和 b 不同时是0
也就说有可能有一个是0.。


题解 首先流星的轨迹没有意义 有意义的是 他出现在矩形内的时间段
所以说 就是抽象出 给出最多n个开区间,最多是因为有的流星永远不会过去。就不算了


把所有开区间 不重叠的画在图数轴上 现在想象 有一个轴
从0开始 一直右移 能交叉的最多的区间就是答案。。
这样就用到神思想了。。触发事件。
每次遇到一个左断点  cnt ++ 右端点 cnt--
还要考虑 如果某个点 同时有左 和 右端点 那肯定要先减再加。。排序的时候要注意。




想清楚了这个 思想后。。 就有点豁然开朗了。
但是还有一个不太好解决的问题。。就是确定一个流星的区间端点 
也就是什么时候进入到矩形 什么时候离开。


第一个要明白 进去的话 要 x 和y 都进去才进去了。
出去的话。一个走了就出去了。

#include<iostream>
#include<cstdio>
#include<algorithm>// use max and min
using namespace std;

const int maxn=100000+10;

struct Event{//事件 有double的时间和 int的 type 0进 1 出
    double t;
    int type;
    bool operator <(const Event & e)const
    {
        return t<e.t || (t==e.t && type > e.type);
    }
} ;
Event event[maxn*2];//注意这里有个乘2  一个流星会触发两事件


void update(int x,int a,int w,double &l,double & r)
{
    if(a==0)//速度向量
    {
        if(x<=0 || x>=w) r=l-1;// no ans
    }
    else if(a>0)//正方向
    {
        l=max(l,-(double)x/a);//x/a是路程除速度 加符号是因为 x在左边 小于0的话 要正的 x大于0的话 就取0 因为本来就在 或者本来就不再
        r=min(r,(double)(w-x)/a);
    }
    else
    {
        l=max(l,(double)(w-x)/a);
        r=min(r,-(double)x/a);
    }
}

int main()
{
        //freopen("/home/gl/in","r",stdin);
        int T;scanf("%d",&T);
        while(T--)
        {
            int w,h,n,num=0,x,y,a,b;
            scanf("%d %d %d",&w,&h,&n);
            for(int i=0;i<n;++i)
            {
                scanf("%d %d %d %d",&x,&y,&a,&b);
                double L=0,R=1e9;//L 和 R传引用 进入 和出去的时间
                update(x,a,w,L,R);//先更新 x在范围内的时间
                update(y,b,h,L,R);//再更新 y在范围内的时间
                if(R>L)//表示有意义
                {
                    event[num++]=(Event){L,0};//注意强制转换的方法
                    event[num++]=(Event){R,1};
                }
            }
            sort(event,event+num);//按照事件出现的顺序排序
            int ans=0,cnt=0;
            for(int i=0;i<num;++i)
            {
                if(event[i].type==0) ans=max(ans,++cnt);
                else cnt--;
            }
            printf("%d\n",ans);
        }
        return 0;
}












评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值