题意:给你一些线段,求没有和其他线段相交的线段数量
公式:p1*p2=(x1*x2,y1*y2)(内积),p1xp2=(x1*y2,x2*y1)(外积)
判断q是否在线段p1-p2上面,根据(p1-q)x(p2-q)=0来判断q是否在直线p1-p2上。
利用内积(p1-q)*(p2-q)<0判断q是否在p1-p2之间。
p1-p2,q1-q2的交点:
(x,y)=p1+(p2-p1)*((q2-q1)x(q1-p1)/((q2-q1)x(p2-p1)));
推理:把p1-p2直线写成点p1+t(p2-p1),然后就是点和q1-q2直线了,最后计算得出上面的。
最后验证交点在两条线段上没有。
这道题主要是精度问题,看着绝对值 <1000,实则在计算的时候会出现超过int,所以需要用Long long,如果是用的double,注意精度,由于全是整数,EPS应该设置大一点,太小的话会由于double精度而wa。double下判断a==0,使用abs(a)<EPS。
#include <iostream> #include<cstdio> #include<cmath> #include<vector> #include<list> #include<cstring> using namespace std; #define EPS 1e-3 #define N 1005 int ans[N]; struct point { double a,b; point(){} point(double a,double b):a(a),b(b){} point operator +(point p) { return point(p.a+a,p.b+b); } point operator -(point p) { return point(a-p.a,b-p.b); } point operator *(double p) { return point(a*p,b*p); } double dot(point p)//内积 { return (p.a*a+p.b*b); } double det(point p)//外积 { return (a*p.b-b*p.a); } }; struct seg { int index; point p1,p2; }; point p1,p2; vector<seg>v; //判断q是否在线段p1-p2上 bool on_str(point p1,point p2,point q) { return (abs((p1-q).det(p2-q))<EPS&&(p1-q).dot(p2-q)<EPS); } //求两直线交点 point intersection(point p1,point p2,point q1,point q2) { return p1+(p2-p1)*((q2-q1).det(q1-p1)/(q2-q1).det(p2-p1)); } void solve(int a) { for(int i=0;i<v.size();i++){ point q1=v[i].p1; point q2=v[i].p2; if(abs((p1-p2).det(q1-q2))<EPS)//判断外积是否为0 { if(on_str(p1,p2,q1)||on_str(p1,p2,q2)||on_str(q1,q2,p1)||on_str(q1,q2,p2))//判断是否有重合 { ans[a]=1; ans[v[i].index]=1; } } else { point r=intersection(p1,p2,q1,q2); if(on_str(p1,p2,r)&&on_str(q1,q2,r)) { ans[a]=1; ans[v[i].index]=1; } } } } int main() { int t; scanf("%d",&t); while(t--) { int n; v.clear(); memset(ans,0,sizeof(ans)); scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%lf%lf%lf%lf",&p1.a,&p1.b,&p2.a,&p2.b); solve(i); v.push_back(seg{i,p1,p2}); } int t=0; for(int i=1;i<=n;i++) { if(ans[i]==0) t++; } printf("%d\n",t); } return 0; }
时间: 2024-10-28 19:39:39