bzoj1007: [HNOI2008]水平可见直线

1007: [HNOI2008]水平可见直线

Time Limit: 1 Sec  Memory Limit: 162 MB

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

以下内容摘自Hzwer的题解

http://hzwer.com/1705.html

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

于是乎,结束了(mengbier)

/**************************************************************
    Problem: 1007
    User: zhouyuyang
    Language: C++
    Result: Accepted
    Time:340 ms
    Memory:3440 kb
****************************************************************/
 
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream> 
#define N 50005
#define eps 1e-9
using namespace std;
struct orz{
    double a,b;//y=ax+b 
    int x;//记录原来位置 
};
orz a[N],st[N];
int ans[N],top,n;
inline bool cmp(orz a,orz b){
    if (fabs(a.a-b.a)<eps) return a.b<b.b;
    return a.a<b.a;
} 
double cross(orz a,orz b){return (b.b-a.b)/(a.a-b.a);}//两直线交点横坐标 
void ins(orz a){
    while (top){
        if (fabs(st[top].a-a.a)<eps) top--;
        else if (top>1&&cross(a,st[top-1])<=cross(st[top],st[top-1])) top--;
        else break;
    }
    st[++top]=a;
}//维护单调栈 
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        scanf("%lf%lf",&a[i].a,&a[i].b);
        a[i].x=i;
    }
    sort(a+1,a+n+1,cmp);
    for (int i=1;i<=n;i++) ins(a[i]);
    for (int i=1;i<=top;i++) ans[st[i].x]=1;
    for (int i=1;i<=n;i++)
        if (ans[i]) printf("%d ",i);
    return 0;
}

 

posted @ 2017-02-15 20:56  zyyoier  阅读(257)  评论(0编辑  收藏  举报