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
解题方法:
我们用一个栈来维护直线间的关系。首先我们按照直线的斜率从小到大进行排序,斜率一样的按照截距从大
到小进行排序,那么我们可以发现对于斜率相同的一组直线,只有截距最大的那个是有可能被看到的,所以我们
先删掉这些不可能的直线。对于当前的直线,我们将它与栈顶直线的交点设为 A,与栈顶的下一条直线的交点设
为 B。我们发现如果 A 的横坐标大于等于 B 的横坐标,那么栈顶直线必被遮盖,所以弹出直到栈顶直线不被遮盖
后,把当前直线入栈,那么最后栈中的所有直线都是可见直线。
交点坐标直接带入直线方程求解即可。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-8;
struct Point{
double a, b;
int id;
void read(){
scanf("%lf%lf", &a, &b);
}
bool operator <(const Point &rhs) const{
if(fabs(a - rhs.a) < eps) return b < rhs.b;
return a < rhs.a;
}
}line[50010], stk[50010];
bool ans[50010];
int n, top;
double cross(Point x, Point y){
return (y.b - x.b) / (x.a - y.a);
}
void ins(Point x){
while(top){
if(fabs(stk[top].a - x.a) < eps) top--;
else if(top > 1 && cross(x, stk[top-1]) <= cross(stk[top], stk[top-1])) top--;
else break;
}
stk[++top] = x;
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++){
line[i].read();
line[i].id = i;
}
sort(line+1, line+n+1);
for(int i = 1; i <= n; i++) ins(line[i]);
for(int i = 1; i <= top; i++) ans[stk[i].id] = i;
for(int i = 1; i <= n; i++){
if(ans[i]){
printf("%d ", i);
}
}
printf("\n");
return 0;
}