题目大意
给定一个矩形照相机,还有哪个流星的起始位置和速度,求能可以照到最多流星的时刻。在边框上的不计入总数。
分析
不难发现流星的轨迹没有任何意义,有意义的只是流星在照相机内部出现的时间段,我们可以把每一个流星经过的时间转化为一个区间,这样的话问题就转化位在最多区间覆盖的点。即,求求出一个数t,使得包含的区间数最多。
我们把"扫描线碰上一个左端点"和“扫描线遇到一个右端点作为”作为一个事件,扫描并且维护,,每当遇到一个左端点,计数器加一,遇到一个右端点计数器进行减一,在这里我们还需要判断扫描线喷上的端点是右端点还是左端点,尤其注意做右端点重合的情况
#include<iostream>
#include<algorithm>
#include<cstdio>
#define maxn 100000+10
using namespace std;
struct event {
double limt;
int type;
bool operator <(const event &a) const
{
return limt < a.limt||(limt==a.limt&&type>a.type);
}
}events[maxn * 2];
void update(int x, int v, int w, double &l, double &r)
{
//cout <<x<<"-"<< w << endl;
if (v == 0)
{
if (x <= 0 || x >= w) {
//cout << "成功生成" << x << endl;
r = l - 1;
}
}
else if (v > 0)
{
l = max(l, -(double)x / v);
r = min(r, (double)(w - x) / v);
}
else
{
r = min(r, -(double)(x) / v);
l = max(l, (double)(w - x) / v);
}
}
int main()
{
int w, h, e;
int t;
cin >> t;
while (t--)
{
int n;
cin >> w >> h>>n;
e = 0;
int x, y, vx, vy;
for (int i = 0; i < n; i++)
{
cin >> x >> y >> vx >> vy;
double l = 0, r = 1e9;
update(x, vx, w, l, r);
update(y, vy, h, l, r);
if (r > l)
{
events[e++] = event({ l, 0 });
events[e++] = event({ r, 1 });
//cout <<x<<"-"<<y<<" "<< l << "-" << r << endl;
}
}
sort(events, events + e);
int cnt = 0, ans = 0;
for (int i = 0; i < e; i++)
if (events[i].type == 0)
ans = max(ans, ++cnt);
else cnt--;
cout << ans << endl;
}
return 0;
}
其实关于蓝书中代码有一点疑问,既然是开区间,为什么扫描至端点就加一减一, 似乎懂了,扫描线看似是端点处加一减一实际上端点之后的状态,而且排序中对于相同值的左右端点,明显右端点的级别高,首先减一,如果是扫描闭区间的话,则相反,对于相同值的左右端点,左端点的优先级最高,先加一