BZOJ1007水平可見直線 計算幾何

@[計算幾何]

Description

在xoy直角坐标平面上有n条直线L1,L2,...Ln,若在y值为正无穷大处往下看,能见到Li的某个子线段,则称Li为
可见的,否则Li为被覆盖的.
例如,对于直线:
L1:y=x; L2:y=-x; L3:y=0
则L1和L2是可见的,L3是被覆盖的.
给出n条直线,表示成y=Ax+B的形式(|A|,|B|<=500000),且n条直线两两不重合.求出所有可见的直线.

Input

第一行为N(0 < N < 50000),接下来的N行输入Ai,Bi

Output

从小到大输出可见直线的编号,两两中间用空格隔开,最后一个数字后面也必须有个空格

Sample Input

3
-1 0
1 0
0 0

Sample Output

1 2

Solution

算法比较直观,先按斜率排序,再将最小的两条线入栈,然后依次处理每条线,如果其与栈顶元素的交点在上一个点的左边,则将栈顶元素出栈 ;这样为什么对呢?因为对如任意一个开口向上的半凸包,从左到右依次观察每条边和每个顶点,发现假如其能被觀察到, 則其斜率不断增大,顶点的横坐标也不断增大。

很簡單的思路就是: 先想到按斜率(節距)排一下序, 然後在紙上畫一下, 大概就知道是怎麼回事了. 注意在斜率相同節距不同時的處理不要出錯.

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
const int N = 1 << 16;
const float EPS = 1e-8;
struct Line
{
	float a, b;
	int ID;
}line[N];
int operator <(Line x, Line y)
{
	if(fabs(x.a - y.a) < EPS)
		return x.b < y.b;
	return x.a < y.a;
}
int top;
Line stack[N];
float get(Line x, Line y)
{
	return (y.b - x.b) / (x.a - y.a);
}
void insert(Line x)
{
	while(top)
	{
		if(fabs(x.a - stack[top - 1].a) < EPS)
			top --;
		else if(top > 1 && (get(x, stack[top - 1]) <= get(stack[top - 1], stack[top - 2])))
			top --;
		else
			break;
	}
	stack[top ++] = x;
}
int ans[N];
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("BZOJ1007.in", "r", stdin);
	freopen("BZOJ1007.out", "w", stdout);
	#endif
	int n;
	scanf("%d", &n);
	for(int i = 0; i < n; i ++)
		scanf("%f%f", &line[i].a, &line[i].b), line[i].ID = i;
	sort(line, line + n);
	top = 0;
	for(int i = 0; i < n; i ++)
		insert(line[i]);
	memset(ans, 0, sizeof(ans));
	for(int i = 0; i < top; i ++)
		ans[stack[i].ID] = 1;
	for(int i = 0; i < n; i ++)
		if(ans[i])
			printf("%d ", i + 1);
}
posted @ 2017-03-01 10:15  Zeonfai  阅读(384)  评论(0编辑  收藏  举报