水平可见直线

来源:互联网 发布:2017淘宝如何提高销量 编辑:程序博客网 时间:2024/05/16 09:42

【题目描述】
在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条直线两两不重合.求出所有可见的直线。
【输入格式】
第一行为N(0 < N < 50000)
接下来的N行输入Ai,Bi
【输出格式】
从小到大输出可见直线的编号,两两中间用空格隔开,最后一个数字后面也必须有个空格
【样例输入】
3
-1 0
1 0
0 0
【样例输出】
1 2
【分析】
如果只有1条直线就太简单了,肯定可以看见。
如果有2条直线,也不难解决,如果两直线平行就遮住了一条,反之都能看见。
接下来的分析不考虑平行。
如果有3条直线,会组成一个三角形,不难发现三角形底边所在直线被遮住了。于是可以得出结论:若斜率最大的直线与斜率次大(或次次大)的直线的交点在斜率次大和斜率次次大的直线的交点的左边,则斜率次大的直线被覆盖。
这里写图片描述
于是可以得出算法:先把所有直线按斜率排序,然后枚举斜率次次大的直线,分别求出与斜率最大和次大的直线的交点,看交点的位置。
可以用栈来维护,有覆盖的就直接弹栈。最后栈里存放的直线就是没有被覆盖的。
至于求交点,可以列方程组:
A1x+B1=y
A2x+B2=y
然后解一下就可以。

#include<cstdio>#include<cmath>#include<algorithm>using namespace std;struct Line {    int k,b,i;//k表示斜率,b表示截距,i表示序号    bool operator < (const Line& rhs) const//重载运算符“<”    {        if (k==rhs.k) return b>rhs.b;        return k<rhs.k;    }} line[50001];int stack[50001]={0},ans[50001];double find_intersection_point(int i,int j) {    int k1=line[i].k, b1=line[i].b, k2=line[j].k, b2=line[j].b;    return (double)(b2-b1)/(k1-k2);//求交点,方程不解释}int main(){    int n,top=1;    scanf("%d",&n);    if (n==1) { printf("1 \n");return 0; }//特判    for (int i=0;i<n;i++) {        int k,b;        scanf("%d%d",&k,&b);        line[i]=(Line){k,b,i+1};    }    sort(line,line+n);//排序    //其实这里少了一步stack[0]=0,即斜率最大的直线先进栈,但是不影响结果    if (line[1].k!=line[0].k) stack[top++]=1;//若两直线不平行则斜率次大的进栈    for (int i=2;i<n;i++) {        if (line[i].k==line[i-1].k) continue;//平行肯定覆盖了        while (top>1 && find_intersection_point(i,stack[top-1])-find_intersection_point(stack[top-2],stack[top-1])<=1e-6) top--;//判断交点        stack[top++]=i;//入栈    }    for(int i=0;i<top;i++) ans[i]=line[stack[i]].i;//找到栈中的直线编号    sort(ans,ans+top);//栈里的直线不一定是按编号递增排列的    for(int i=0;i<top;i++) printf("%d ",ans[i]);    return 0;}
3 0
原创粉丝点击