题意:求是否存在一条直线,使所有线段到这条直线的投影至少有一个交点。
思路:计算几何。这道题要思考到两点:
1:把问题转化为是否存在一条直线与每条线段都有交点。证明:若存在一条直线l和所有线段相交,作一条直线m和l垂直,则m就是题中要求的直线,所有线段投影的一个公共点即为垂足。
2:枚举两两线段的各一个端点,连一条直线,再判断剩下的线段是否都和这条直线有交点。证明:若有l和所有线段相交,则可保持l和所有线段相交,左右平移l到和某一线段交于端点停止(“移不动了”)。然后绕这个交点旋转。也是转到“转不动了”(和另一线段交于其一个端点)为止。这样就找到了一个新的l满足题意,而且经过其中两线段的端点。
ps:要学会这种从结果反过来证的思考方法....
我的判断线段与直线l是否相交的方法:
1:利用叉积的性质,判断线段的两个端点是否在直线的两边。
2:求线段所在的直线tmp,求tmp与l的交点p,由线段两端点到p的距离之和,与线段的距离比较,若相等则证明线段与直线相交。
参考博客:http://blog.sina.com.cn/s/blog_6635898a0100n2lv.html
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <queue> #include <map> #include <vector> #include <set> #include <string> #include <math.h> using namespace std; const double eps = 1e-8; int sgn(double x) { if(fabs(x) < eps)return 0; if(x < 0) return -1; return 1; } struct Point { double x,y; Point() {} Point(double _x,double _y) { x = _x; y = _y; } Point operator -(const Point &b)const { return Point(x - b.x,y - b.y); } double operator ^(const Point &b)const { return x*b.y - y*b.x; } double operator *(const Point &b)const { return x*b.x + y*b.y; } }; struct Line { Point s,e; Line() {} Line(Point _s,Point _e) { s = _s; e = _e; } }; double xmult(Point p0,Point p1,Point p2) //p0p1 X p0p2 { return (p1-p0)^(p2-p0); } bool Seg_inter_line(Line l1,Line l2) //判断直线l1和线段l2是否相交 { return sgn(xmult(l2.s,l1.s,l1.e))*sgn(xmult(l2.e,l1.s,l1.e)) <= 0; } double dist(Point a,Point b) { return sqrt( (b - a)*(b - a) ); } const int MAXN = 110; Line line[MAXN]; bool check(Line l1,int n) { if(sgn(dist(l1.s,l1.e)) == 0 )return false; for(int i = 0; i < n; i++) if(Seg_inter_line(l1,line[i]) == false) return false; return true; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int n; int T; scanf("%d",&T); while(T--) { scanf("%d",&n); double x1,y1,x2,y2; for(int i = 0; i < n; i++) { scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); line[i] = Line(Point(x1,y1),Point(x2,y2)); } bool flag = false; for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) if(check(Line(line[i].s,line[j].s),n) || check(Line(line[i].s,line[j].e),n) || check(Line(line[i].e,line[j].s),n) || check(Line(line[i].e,line[j].e),n) ) { flag = true; break; } if(flag) printf("Yes!\n"); else printf("No!\n"); } return 0; }