UVALive 3905-Meteor-扫描线算法

http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=16454

题意 

给一个(0,0)到(w,h)的矩形,

给n个流星位置(x,y),以及他们往的方向(a,b),流星的轨迹会是(x,y)-》(a,b)射线

X=x+a*t

Y=y+b*t

求某一时刻 矩形内星星最多的个数

那么显然我们要求的是流星的射线轨迹在矩形内的时间,也就是把上面两个公式的X,Y代入0,w(h)解得一个时间范围【L,R】,显然R<L舍弃,

然后我们求得所有的【L,R】(开区间,因为题目说,在边缘的星星不合法)


我们把这些开区间看成一条条线段

我们要找的答案就是 实数轴上 被这些线段重叠次数最多的 点


我们把每个区间看成一个事件, 把左端点看成事件1,右端点为事件2。

按端点坐标排序,端点坐标相同,右端点优先(如果左端点有限,由于是开区间,计算过程会比实际答案大)

然后遍历,遇到事件1,计数器++,else 计数器 --

最后输出maxx


(注意,时间的区间范围,的R最大值是 w-x或x)

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;

const double pi=acos(-1.0);
double eps=0.000001; 
double max(double a,double b)
{return a>b?a:b;}
double min(double a,double b)
{return a<b?a:b;}

struct node
{
	double x;
	int type;
};
node tm[200005];
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)
	{
		R=min(R,1.0*(w-x)/a);
		L=max(L,-x*1.0/a);
	}
	else
	{
		L=max(L,1.0*(w-x)/a);
		R=min(R,-x*1.0/a);
	}
}
bool cmp(node a,node b)
{
	if (fabs(a.x-b.x)>eps)
		return a.x<b.x;
	else
		return a.type>b.type;
}
int main()
{
	int i,t;  
	cin>>t;
	while(t--)
	{
		int x,y,a,b;
		int ok=0;
		int w,h,n;
		cin>>w>>h>>n;
		for (i=1;i<=n;i++)
		{
			double L=0,R=3e5+5;	//注意R的最大值是(w-x)或x,1e5+2e5=3e5
			scanf("%d%d%d%d",&x,&y,&a,&b);
			update(x,a,w,L,R); 
			update(y,b,h,L,R);
			// 0<x+a*t<w
			// 0<y+b*t<h
			if (R<=L) continue; 
			tm[++ok].x=L;
			tm[ok].type=1;
			tm[++ok].x=R;
			tm[ok].type=2;
		}
		sort(tm+1,tm+1+ok,cmp);
	int maxx=0;
	int cun=0;
		for (i=1;i<=ok;i++)
		{

			if (tm[i].type==1)
				cun++;
			else
				cun--;
			if (cun>maxx) maxx=cun;
		}
		printf("%d\n",maxx);
	}
	   
	return 0;
	
} 



你可能感兴趣的:(UVALive 3905-Meteor-扫描线算法)