【半平面交】BZOJ1007(HNOI2008)[水平可见直线]题解

来源:互联网 发布:国内人工智能专业排名 编辑:程序博客网 时间:2024/06/05 06:30

题目概述

二维直角坐标系中,给出n条直线,从y轴正无穷大处向下看,输出可以看见的直线。

解题报告

全部搞在一起看不可能分析清楚,肯定要先两两分析:
这里写图片描述
设交点是(x,y),那么对于斜率小的直线,大于等于x的部分都被遮住了,而对于斜率大的直线,小于等于x的部分都被遮住了。

假设一条直线小于等于x1,x2,x3,,xn的部分都被遮住了,大于等于y1,y2,y3,ym的部分都被遮住了。设x的最大值为xmax,y的最小值为ymin,只要xmax>=ymin,这条直线就看不到了。

于是问题转化为求xmaxyminO(n2)肯定是可以的,但是效率太低。而且由于y=k1x+b1,y=k2x+b2的交点坐标x=(b2b1)/(k1k2),同时被k和b影响,常用的求极值方法派不上用场,所以我们要想其他方法。
……
反正我是没想到!所以上述想法很难实现!看了讨论发现了个关键词:半平面交。虽然我不太了解半平面交,但是我发现不一定要用代数做法,可以用几何做法解决这道题。

先按照k排序(k相同的只留下b最大的),然后储存一个栈,表示目前能看见的直线。每次加入一条直线时,求出这条直线与栈顶的交点A,然后求出栈顶和栈顶下面元素的交点B,如果A的横坐标小于等于B的横坐标,那么当前栈顶就被遮掉了(也就是简化版半平面交):
这里写图片描述
最后栈内存在的元素就可见直线。

示例程序

#include<cstdio>#include<cmath>#include<algorithm>using namespace std;const int maxn=50000;int n,top,stk[maxn+5];struct Point{    double x,y;    Point (double X=0,double Y=0) {x=X;y=Y;}};int fcmp(double x,double y) {if (fabs(x-y)<=1e-10) return 0;if (x<y) return -1; else return 1;}struct Line{    double k,b;int id;    bool operator < (const Line &a) const {return fcmp(k,a.k)<0||!fcmp(k,a.k)&&fcmp(b,a.b)>0;}};Line l[maxn+5];Point getPoint(const Line &a,const Line &b) {double x=(b.b-a.b)/(a.k-b.k);return Point(x,a.k*x+a.b);}int main(){    freopen("program.in","r",stdin);    freopen("program.out","w",stdout);    scanf("%d",&n);    for (int i=1;i<=n;i++) scanf("%lf%lf",&l[i].k,&l[i].b),l[i].id=i;    sort(l+1,l+1+n);    int num=1;for (int i=2;i<=n;i++) if (l[i-1].k<l[i].k) l[++num]=l[i];n=num;    for (int i=1;i<=n;i++)    {        while (top>1&&fcmp(getPoint(l[stk[top]],l[i]).x,getPoint(l[stk[top-1]],l[stk[top]]).x)<=0) top--;        //删除堆顶        stk[++top]=i;    }    for (int i=1;i<=top;i++) stk[i]=l[stk[i]].id;    sort(stk+1,stk+1+top);    for (int i=1;i<=top;i++) printf("%d ",stk[i]);    return 0;}
0 0
原创粉丝点击