【题意】
有n条线段(编号为1~n)(n<=10000),按1~n的顺序放在二维坐标系上(就是先放1号,再放2号……),要求输出最上面的那些线段的编号(就是没有其他线段压在它上面的那些线段)
题解:
数据较弱,可以使用暴力,但如何判断两线段相交,本题题意是用叉积判断。
基本知识:叉积>0,为逆时针转,叉积<0,为顺时针转。
如下图,
两端点为p1,p2的线段l1与两端点为p3,p4的线段l2相交时,以p1为基准点,p3转向p2的方向与p2转向p4的方向是一样的,也就是说两叉积之积大于0。但这还不够,还需要判断以p3为基准点的p1,p4,p2的旋转方向,因为当p1在l2的左边时,也能满足条件1。
还有特殊情况,就是线段的一个端点在另一条线段所在的直线上,那么此时叉积为0,此时判断该点是否在线段上即可。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
struct node{double x,y;};
struct line{node p1,p2;}L[10010];
double multi(node p1,node p2,node p0)
{
double x1,y1,x2,y2;
x1=p1.x-p0.x;
y1=p1.y-p0.y;
x2=p2.x-p0.x;
y2=p2.y-p0.y;
return x1*y2-x2*y1;
}
int n;
bool check(line l1,line l2)
{
node p1,p2,p3,p4;
p1=l1.p1;p2=l1.p2;p3=l2.p1;p4=l2.p2;
if(multi(p3,p2,p1)*multi(p2,p4,p1)>0.0&&multi(p1,p4,p3)*multi(p4,p2,p3)>0.0)return true;
if(multi(p1,p3,p4)==0.0&&min(p3.x,p4.x)<=p1.x&&p1.x<=max(p3.x,p4.x))return true;
if(multi(p2,p3,p4)==0.0&&min(p3.x,p4.x)<=p2.x&&p2.x<=max(p3.x,p4.x))return true;
if(multi(p3,p1,p2)==0.0&&min(p1.x,p2.x)<=p3.x&&p3.x<=max(p1.x,p2.x))return true;
if(multi(p4,p1,p2)==0.0&&min(p1.x,p2.x)<=p4.x&&p4.x<=max(p1.x,p2.x))return true;
return false;
}
int main()
{
int ans[10010],l=0;
bool f[10010];
memset(f,true,sizeof(f));
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lf%lf%lf%lf",&L[i].p1.x,&L[i].p1.y,&L[i].p2.x,&L[i].p2.y);
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++)
if(check(L[i],L[j])){f[i]=false;break;}//重要优化!!!
for(int i=1;i<=n;i++)
if(f[i])ans[++l]=i;
for(int i=1;i<=l;i++)printf("%d ",ans[i]);
}