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; }
我是蒟蒻,膜拜神犇,orz