【BZOJ1007】水平可见直线(HNOI2008)-栈+凸壳

来源:互联网 发布:怎么用万捷网络验证 编辑:程序博客网 时间:2024/06/07 03:02

测试地址:水平可见直线
做法:本题需要用到栈+凸壳。
通过观察,我们知道从y轴正方向向下看,轮廓肯定是一个下凸壳,所以我们把所有直线按斜率从小到大排序,每次插入一条直线,可以知道新插入的直线肯定会在当前的凸壳中,那么我们执行下列的过程:若当前栈顶的两条直线交点在当前直线的下方或在当前直线上,就说明栈顶的直线不会在最后的凸壳中,将其弹出栈顶,然后继续这样判断栈顶的两条直线,以此类推,直到不满足以上条件或者栈内只有一条直线为止,然后插入当前直线到栈顶。最后只要把在栈中的所有直线的编号从小到大输出即可。注意有直线斜率相等的情况下,只取同斜率直线中截距最大的那一个,不然计算交点时会产生错误。时间复杂度为O(nlogn)
以下是本人代码:

#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>#include <cmath>#define ll long long#define eps 1e-8using namespace std;int n,st[50010],top;bool vis[50010]={0};struct line{    double a,b;    int id;}l[50010];struct point{    double x,y;};bool cmp(line a,line b){    return a.a<b.a;}point inter(line i,line j){    point a;    a.x=(j.b-i.b)/(i.a-j.a);    a.y=i.a*a.x+i.b;    return a;}bool check(int i,int j,int k){    point a=inter(l[i],l[j]);    if ((l[k].a*a.x+l[k].b)>=a.y) return 1;    else return 0;}int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++)    {        l[i].id=i;        scanf("%lf%lf",&l[i].a,&l[i].b);    }    sort(l+1,l+n+1,cmp);    top=0;    for(int i=1;i<=n;i++)    {        if (top>0&&fabs(l[st[top]].a-l[i].a)<eps)        {            if (l[st[top]].b<l[i].b) top--;            else continue;        }        while(top>=2&&check(st[top-1],st[top],i)) top--;        st[++top]=i;    }    for(int i=1;i<=top;i++)        vis[l[st[i]].id]=1;    for(int i=1;i<=n;i++)        if (vis[i]) printf("%d ",i);    return 0;}
阅读全文
0 0
原创粉丝点击