题意:
在一个二维平面上有n个流星,每个流星有自己的初始位置和速度,有一个照相机,张相机的可视范围是一个矩形框,左下角(0,0)右上角(w ,h),然后问你相机的矩形内出现的最多的流星数是多少?
思路:
感觉是一道很不错的题目,想到流星数目,第一反应就是可以把他转化成时间段,我们求出每个流星的进入相机时间,和出相机时间,这样就会得到一个时间段,流星的最大
数量也就是时间段的最大重叠部分,这个不解释了应该不难理解,现在问题就来了,我们怎么求得所有的时间段呢?一开始我也感觉很麻烦,然后在白书上学了一个比较方便的方法,我们求时间段可以先把流星分解了,x,y单独算,进入的时间肯定是进x进y的最大值,出去的时间肯定是出x,出y的最小值,这样我们分别求完之后就ok了,如果L>=R就证明没有在相机范围内出现过,下面给出求L,R的代码
void Update(int x ,int a ,int w ,double& L ,double& R)
{
if(a == 0)
{
if(x <= 0 || x >= w) R = L - 1; //永远也进步了相机
}
else if(a > 0)//往上跑
{
L = Max(L ,-(double)x / a);
R = Min(R ,(double)(w - x) / a);
}
else//往下跑
{
L = Max(L ,(double)(w - x) / a);//理解不了的注意这里的a是负的
R = Min(R ,-(double)x / a);
}
}
求x,y的时候都是用的上面的那个,只不过传的参数不一样罢了,具体细节可以看代码。
这样我们就得到了所有流星的时间段L,R(L>=R的是不满足的,直接丢弃),然后就是求区间的最大重叠面积了,这个也比较好求,如果你不嫌麻烦可以写线段树贪心去求,时间复杂度是O(n*log(n))的,不过有一个更省事更快的方法,就是扫面线法O(n)<其实这么说有漏洞,因为扫描线涉及到排序,排序是O(n*log(n))的,这个地方大家知道就行了>,我们可以把所有端点都扔进结构体数组里,然后按照时间从小到大排序,如果时间相等那么就后端点在前面(这样的原因是题目中说在相机边框上的不算,如果算的话就入端点在前面),排序之后直接扫一遍,遇到前端点就++,后端点就--,过程中最大的值就是答案,这个应该很好理解,不理解的在纸上画一画,每次变化的时候都是在端点上变化的,这个就是经典的扫描线想法,扫描线也可以配合着线段树应用,求重叠面积,体积,周长啥的。这个题目还有一个小小的优化(白书上说的),我们可以躲开浮点运算,因为整个程序里涉及到的除法就是除以速度,速度的范围是绝对值<=10,那么我们直接把被除数扩大1,2,3..10的最小公倍数2520倍就行了,这样保证都是整除,至于为什么我想不用我解释了。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100000 + 10
using namespace std;
typedef struct
{
int mk;
double time;
}NODE;
NODE node[N+N];
bool camp(NODE a ,NODE b)
{
return a.time < b.time || a.time == b.time && a.mk < b.mk;
}
double Max(double x ,double y)
{
return x > y ? x : y;
}
double Min(double x ,double y)
{
return x < y ? x : y;
}
void Update(int x ,int a ,int w ,double& L ,double& R)
{
if(a == 0)
{
if(x <= 0 || x >= w) R = L - 1;
}
else if(a > 0)
{
L = Max(L ,-(double)x / a);
R = Min(R ,(double)(w - x) / a);
}
else
{
L = Max(L ,(double)(w - x) / a);
R = Min(R ,-(double)x / a);
}
}
int main ()
{
int t ,w ,h ,n ,i;
int x ,y ,a ,b;
scanf("%d" ,&t);
while(t--)
{
scanf("%d %d" ,&w ,&h);
scanf("%d" ,&n);
int nowid = 0;
for(i = 1 ;i <= n ;i ++)
{
scanf("%d %d %d %d" ,&x ,&y ,&a ,&b);
double L = 0 ,R = 999999999;
Update(x ,a ,w ,L ,R);
Update(y ,b ,h ,L ,R);
if(L < R)
{
node[++nowid].time = L;
node[nowid].mk = 1;
node[++nowid].time = R;
node[nowid].mk = -1;
}
}
int Ans = 0;
sort(node + 1 ,node + nowid + 1 ,camp);
int sum = 0;
for(i = 1 ;i <= nowid ;i ++)
{
sum += node[i].mk;
if(Ans < sum) Ans = sum;
}
printf("%d
" ,Ans);
}
return 0;
}
//排除浮点型运算
void Update(int x ,int a ,int w ,int& L ,int& R)
{
if(a == 0)
{
if(x <= 0 || x >= w) R = L - 1;
}
else if(a > 0)
{
L = Max(L ,-x * 2520 / a);
R = Min(R ,(w - x) * 2520 / a);
}
else
{
L = Max(L ,(w - x) * 2520/ a);
R = Min(R ,-x * 2520/ a);
}
}