G50 叉积应用 线线关系

视频链接:571 叉积应用 线线关系【计算几何】_哔哩哔哩_bilibili

1. POJ2653 Pick-up sticks

题意:按顺序给出棍子,上面的可能会覆盖下面的,求出不被其他棍子覆盖的所有棍子

思路:

  暴力搜索,如果一条线段不被后面任一条覆盖,则它是答案之一

  叉积判断两线段是否相交

时间: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;
}

2. POJ 3304 Segments

题意:找出一条直线,使得给出的所有线段在这个直线上的投影有交集

思路:
  如果可以找到一个直线与所有的线段都相交,那么这个直线的垂线满足题意
  既然这条直线穿过所有线段,转一下这条直线,则必经过两个线段的端点
  所以枚举经过所有端点的直线,然后对于每条直线,再枚举所有线段。用叉积判断直线与线段是否相交

  特判,重合点不能确定直线,所以过掉

时间: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;
}

3. POJ2074 Line of Sight

题意:给出一个房子的两个端点坐标,以及观察线的左右端点坐标,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; }

 

posted @ 2023-02-26 13:32  董晓  阅读(371)  评论(0编辑  收藏  举报