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

题目概述

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

解题报告

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

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

于是问题转化为求 xmax ymin O(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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值