题目描述
给定N个线段。求有交点的线段对数。
保证没有两条线段共线
输入
一行一个整数N,表示线段的个数
第2~N+1行,每行四个实数,x1,y1,x2,y2,表示线段的两个端点(x1,y1)和(x2,y2)
输出
一行一个整数,表示有交点的线段对数。
样例输入
复制样例数据 3
0.00 0.00 1.00 1.00
0.00 1.00 1.00 0.00
0.00 0.00 1.00 0.00
样例输出
3
提示
(0,0)(1,1)和(0,1)(1,0)有交点
(0,0)(1,1)和(0,0)(1,0)有交点
(0,1)(1,0)和(0,0)(1,0)有交点
对于100%的数据,N≤100
点的坐标范围(−10000,10000)
题解: 本题主要是要注意直斜率不存在的情况,重合点和精度问题,前者特判,后者通过set和通过坐标放大的方法来解决精度问题
#include <iostream>
#include<cmath>
#include<set>
using namespace std;
struct node
{
double x1,y1,x2,y2;
double a,b,d;
double sign=1;
}p[105];
struct Node
{
double x,y;
bool operator<(const Node& tmp)const///重载<
{
if(this->x!=tmp.x||this->y!=tmp.y) return true;
else return false;
}
}N;
set<Node>s;///交点集合
int main()
{
int n;cin>>n;
for(int i=0;i<n;i++)
{
cin>>p[i].x1>>p[i].y1>>p[i].x2>>p[i].y2;
p[i].x1*=100,p[i].y1*=100,p[i].x2*=100,p[i].y2*=100;///可以通过将原来的数放大来提高精度
double X=p[i].x2-p[i].x1;
if(X!=0)
{
p[i].a=(p[i].y2-p[i].y1)/(X);
p[i].b=p[i].y1-p[i].a*p[i].x1;
double y=p[i].y2-p[i].y1,x=p[i].x2-p[i].x1;
p[i].d=sqrt(y*y+x*x);
}
else {p[i].sign=0;p[i].d=abs(p[i].y1-p[i].y2);}
//cout<<"i="<<i<<' '<<p[i].a<<' '<<p[i].b<<endl;
}
for(int i=0;i<n;i++)
{
for(int j=i+1;j<n;j++)
{
///交点(N.x,N.y)
if(p[i].sign!=0&&p[j].sign!=0)
{
N.x=(p[j].b-p[i].b)/(p[i].a-p[j].a);
N.y=p[i].a*N.x+p[i].b;
//
}
else
{
if(p[i].sign==0)
N.x=p[i].x1,N.y=p[j].a*N.x+p[j].b;
if(p[j].sign==0)
N.x=p[j].x1,N.y=p[i].a*N.x+p[i].b;
//cout<<N.x<<' '<<N.y<<endl;
}
///判断交点是否在两个线段上,交点到两个点的距离都小于线段长度
double Newx11=N.x-p[i].x1,Newy11=N.y-p[i].y1;
double D11=sqrt(Newx11*Newx11+Newy11*Newy11);
double Newx12=N.x-p[i].x2,Newy12=N.y-p[i].y2;
double D12=sqrt(Newx12*Newx12+Newy12*Newy12);
double Newx21=N.x-p[j].x1,Newy21=N.y-p[j].y1;
double D21=sqrt(Newx21*Newx21+Newy21*Newy21);
double Newx22=N.x-p[j].x2,Newy22=N.y-p[j].y2;
double D22=sqrt(Newx22*Newx22+Newy22*Newy22);
if(D11<=p[i].d&&D21<=p[j].d&&D12<=p[i].d&&D22<=p[j].d)
{
s.insert(N);
//cout<<111<<endl;
}
//cout<<N.x<<' '<<N.y<<endl;
}
}
cout<<s.size()<<endl;
}