2984. 线段

题意

在二维平面内有 n 条线段,请你编写一个程序,判断是否存在一条直线满足将这 n 条线段投影到该直线上后,所有的投影线段至少具有一个公共点。

思路

转化题意
我们发现如果可以找到一个直线与所有的线段都相交;
那么所有的线段的投影一定都交于这个直线与其垂线的垂足处。

那么问题就转化为如何找一个可以穿过所有线段的直线?

我们肯定不能暴力找所有的坐标判断是否相交。这里我们使用一个计算几何常用的技巧:旋转直线。

我们可以先找一个过一个线段的直线,然后我们旋转他。
我们把这个直线的定点定为这个线段的端点,然后旋转。因为我们要满足能够与其他的线段相交,所以我们旋转的时候有一个限制,就是不能超过另一个线段的一个端点,所以我们可以把所有的候选直线限定到所有线段的端点的连线的直线。

所以我们可以\(O(n^2)\),枚举所有线段的端点,然后 \(O(n)\) 暴力判断是否都与其他的线段有交点,如果存在一个直线与所有的线段有交点说明一定存在解,输出 Yes!。

那么现在的问题转化为我们如何判断一个线段和一个直线是否有交点
判断线段的两个端点是否在直线的异侧即可。

代码


#include<bits/stdc++.h>
#define int long long
#define pdd pair<double,double> 
#define x first
#define y second
using namespace std;
const int N = 210;
const double eps=1e-8;

int n;
pdd a[N],b[N],q[N];

int sign(double x){
  if(abs(x)<eps) return 0;
  if(x<0) return -1;
  else return 1;
}
int cmp(double x,double y){
  if(abs(x-y)<eps) return 0;
  if(x<y) return -1;
  else return 1;
}
double cross(double x1,double y1,double x2,double y2){
  return x1*y2-x2*y1;
}
double area(pdd a,pdd b,pdd c){
  return cross(b.x-a.x,b.y-a.y,c.x-a.x,c.y-a.y);
}
bool check(){
  for(int i=0;i<n*2;i++)
    for(int j=i+1;j<n*2;j++){
      if(!cmp(q[i].x,q[j].x) && !cmp(q[i].y,q[j].y)) continue;
      bool flag=true;
      for(int k=0;k<n;k++){
        if(sign(area(q[i],q[j],a[k])) * sign(area(q[i],q[j],b[k]))>0)
        {
          flag=false;
          break;
        }
      }
      if(flag) return true;
    }
return false;    
}
void solve(){
  cin>>n;
  for(int i=0,k=0;i<n;i++) {
    double x1,y1,x2,y2;
    cin>>x1>>y1>>x2>>y2;
     q[k++]={x1, y1}, q[k++]={x2, y2};
    a[i]={x1,y1},b[i]={x2,y2};
  }
 

  if(check()) puts("Yes!");
  else puts("No!");
} 

signed main(){
  int t=1;
  cin>>t;
  while(t--)
  solve();
  return 0;
}

原文:
作者:繁凡さん
链接:https://www.acwing.com/solution/content/26937/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

posted @ 2022-09-12 20:53  kingwzun  阅读(25)  评论(0编辑  收藏  举报