这个题目也是以前看过的 当时没有敲的勇气。今天看懂了。
题目实在是太巧妙了啊。。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;
}