G50 叉积应用 线线关系
视频链接:571 叉积应用 线线关系【计算几何】_哔哩哔哩_bilibili
题意:按顺序给出棍子,上面的可能会覆盖下面的,求出不被其他棍子覆盖的所有棍子
思路:
暴力搜索,如果一条线段不被后面任一条覆盖,则它是答案之一
叉积判断两线段是否相交
时间:O(n*n*T)
#include<cstdio> #include<cstring> #include<cmath> #define N 100005 using namespace std; struct Point{double x,y;} a[N],b[N]; int n,ans[N],cnt; double cross(Point a,Point b,Point c){ //叉积 return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x); } bool intersect(int i, int j){ //是否相交 if(cross(a[i],b[i],a[j])*cross(a[i],b[i],b[j])>0)return 0; if(cross(a[j],b[j],a[i])*cross(a[j],b[j],b[i])>0)return 0; return 1; } void solve(){ printf("Top sticks: "); cnt=0; for(int i=1; i<=n; i++){ //枚举棍子i bool flag=0; for(int j=i+1; j<=n; j++) //枚举棍子j if(intersect(i,j)){flag=1; break;} if(!flag) ans[++cnt]=i; } for(int i=1;i<cnt;i++) printf("%d, ",ans[i]); printf("%d",ans[cnt]); puts("."); } int main(){ while(scanf("%d",&n),n){ for(int i=1; i<=n; i++) scanf("%lf%lf%lf%lf",&a[i].x,&a[i].y,&b[i].x,&b[i].y); solve(); } return 0; }
题意:找出一条直线,使得给出的所有线段在这个直线上的投影有交集
思路:
如果可以找到一个直线与所有的线段都相交,那么这个直线的垂线满足题意
既然这条直线穿过所有线段,转一下这条直线,则必经过两个线段的端点
所以枚举经过所有端点的直线,然后对于每条直线,再枚举所有线段。用叉积判断直线与线段是否相交
特判,重合点不能确定直线,所以过掉
时间:O(n*n*n*T)
#include <iostream> #include <cstring> #include <algorithm> #include <cmath> using namespace std; #define x first #define y second const int N=210; typedef pair<double,double> Point; Point a[N], b[N], p[N]; int n; double cross(Point a, Point b, Point c){ //叉积 return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x); } bool check(){ for(int i=0; i<n*2; i++){ for(int j=i+1; j<n*2; j++){ //枚举所有直线 if(p[i].x==p[j].x && p[i].y==p[j].y)continue; //重合点 bool flag=true; for(int k=0; k<n; k++){ //枚举所有线段 if(cross(p[i],p[j],a[k])*cross(p[i],p[j],b[k])>0){ flag=false; break; //线段与直线不相交,则该直线不行 } } if(flag) return true; //找到一个可行解就返回true } } return false; //所有直线都不行,返回false } int main(){ int T; double x1, y1, x2, y2; scanf("%d", &T); while(T--){ scanf("%d", &n); for(int i=0, k=0; i<n; i++){ scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2); p[k++]=Point(x1, y1);p[k++]=Point(x2, y2); //所有端点 a[i]=Point(x1, y1); b[i]=Point(x2, y2); //线段端点 } if(check()) puts("Yes!"); else puts("No!"); } return 0; }
题意:给出一个房子的两个端点坐标,以及观察线的左右端点坐标,n个障碍物,问观察线上能看到完整的房子的最长连续区间
思路:
求出每个障碍物导致的视野盲区的左右端点,用叉积求直线的交点即可
将所有的盲区的按端点排序,跑区间合并,维护右端点,更新答案
特判,如果障碍物不在房子和观察线之间,不需要考虑
时间:O(nlogn*T)
#include <iostream> #include <cstring> #include <algorithm> #include <cmath> #define x first #define y second using namespace std; const int N=5005; int n,cnt; typedef pair<double,double> Point; Point H1,H2,L1,L2,B1,B2, a[N]; Point operator+(Point a,Point b){ //向量+ return Point(a.x+b.x,a.y+b.y); } Point operator-(Point a, Point b){ //向量- return Point(a.x-b.x,a.y-b.y); } Point operator*(Point a,double t){ //数积 return Point(a.x*t,a.y*t); } double operator*(Point a, Point b){ //叉积 return a.x*b.y-a.y*b.x; } double getNode(Point a,Point u,Point b,Point v){ //直线交点
double t=(a-b)*v/(v*u); Point p=a+u*t; if(p.x>L2.x) p.x=L2.x; //边界约束 if(p.x<L1.x) p.x=L1.x; return p.x; } int main(){ while(scanf("%lf%lf%lf",&H1.x,&H2.x,&H1.y),H1.x&&H2.x&&H1.y){ H2.y=H1.y; //H:房子 scanf("%lf%lf%lf",&L1.x,&L2.x,&L1.y); L2.y=L1.y; //L:观察线 scanf("%d",&n); cnt=0; for(int i=1; i<=n; ++i){ scanf("%lf%lf%lf",&B1.x,&B2.x,&B1.y);B2.y=B1.y; //B:障碍物 if(B1.y<L1.y || B1.y>H1.y) continue; //特判不遮挡 a[cnt].x=getNode(H2,B1-H2,L1,L2-L1); //盲区左端点 a[cnt++].y=getNode(H1,B2-H1,L1,L2-L1); //盲区右端点 } sort(a,a+cnt); //盲区端点排序 double ans=max(a[0].x-L1.x,L2.x-a[cnt-1].y); double r=a[0].y; for(int i=1; i<cnt; ++i){ //区间合并 if(a[i].x>r) ans=max(ans,a[i].x-r); r=max(r,a[i].y); } if(cnt==0) ans=L2.x-L1.x; //特判无遮挡情况 if(ans==0) puts("No View"); else printf("%.2f\n",ans); } return 0; }