POJ3304 Segments 【线段直线相交】
题意:
给出n条线段两个端点的坐标,问所有线段投影到一条直线上,如果这些所有投影至少相交于一点就输出Yes!,否则输出No!。
思路:
计算几何。这道题要思考到两点:
1:把问题转化为是否存在一条直线与每条线段都有交点。证明:若存在一条直线l和所有线段相交,作一条直线m和l垂直,则m就是题中要求的直线,所有线段投影的一个公共点即为垂足。
2:枚举两两线段的各一个端点,连一条直线,再判断剩下的线段是否都和这条直线有交点。证明:若有l和所有线段相交,则可保持l和所有线段相交,左右平移l到和某一线段交于端点停止(“移不动了”)。然后绕这个交点旋转。也是转到“转不动了”(和另一线段交于其一个端点)为止。这样就找到了一个新的l满足题意,而且经过其中两线段的端点。
判断线段与直线l是否相交的方法:
1:利用叉积的性质,判断线段的两个端点是否在直线的两边。
2:求线段所在的直线tmp,求tmp与l的交点p,由线段两端点到p的距离之和,与线段的距离比较,若相等则证明线段与直线相交。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cmath> using namespace std; const int maxn = 105; const double eps = 1e-8; int n; struct Point { double x, y; }s[maxn], e[maxn]; double mult(Point sp, Point ep, Point op) { return (sp.x-op.x)*(ep.y-op.y) - (ep.x-op.x)*(sp.y-op.y); } bool findd(Point p1, Point p2) { if(abs(p1.x-p2.x) < eps && abs(p1.y-p2.y) < eps) return false; for(int i = 0; i < n; i ++) if(mult(p1, p2, s[i])*mult(p1, p2, e[i]) > eps) return false; return true; } int main() { int t, i, j; cin >> t; while(t --) { cin >> n; for(i = 0; i < n; i ++) cin >> s[i].x >> s[i].y >> e[i].x >> e[i].y; bool flag = false; if(n < 3) flag = true; for(i = 0; i < n && !flag; i ++) for(j = i + 1; j < n && !flag; j ++) // 枚举线段的端点。 { if(findd(s[i], s[j])) flag = true; else if(findd(s[i], e[j])) flag = true; else if(findd(e[i], s[j])) flag = true; else if(findd(e[i], e[j])) flag = true; } if(flag) cout << "Yes!" << endl; else cout << "No!" << endl; } return 0; }