题目链接:http://poj.org/problem?id=1039
题意:给一条曲折的管道,给出的形式是每一个转弯处口的坐标,求光线从入口进入能到的最远的x点,如果能穿过管子则输出“Through all the pipe.”
输入:
4
0 1
2 2
4 1
6 4
6
0 1
2 -0.6
5 -4.45
7 -5.57
12 -10.8
17 -16.55
0
每个样例第一行n表示n个折点,下面n行。
参考博客:http://blog.csdn.net/lin375691011/article/details/17800529
首先要知道:
1、可以通过两向量的叉积来判断是否相交。具体见《算法艺术与信息学竞赛》P348开始向后很多页。
2、一条光线从入口到某一点必然会擦过一个上点(管转弯口上壁的点)和一个下点(管转弯口下壁的点)。
3、如果一条光线自始至终未擦到任何定点,这条光线是可以“优化的”。
4、“优化”就是可以通过旋转使它擦过一个上点和一个下点。
5、2、3、4点详见《算法艺术与信息学竞赛》P359.
解题思路:
1、先枚举光线是否能从入口射入,如果能判断能从第一个转弯处开始能穿过几个(判断能与多少个转弯处所在的竖直直线相交)。
2、如果遇到某一个转弯处穿不过了,计算入口到相交管壁的距离,有可能是上管壁,也有可能是下管壁,保存最大值。
3、如果能穿过所有的转弯处,就直接输出“Through all the pipe.”,否则输出最长距离。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
const double eps = 1e-8;
const double PI = acos(-1.0);
int sgn(double x) {
if(fabs(x) < eps)return 0;
if(x < 0)return -1;
else 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;
}
void transXY(double B) {
double tx = x,ty = y;
x = tx*cos(B) - ty*sin(B);
y = tx*sin(B) + ty*cos(B);
}
};
struct Line {
Point s,e;
Line(){}
Line(Point _s,Point _e) {
s = _s;e = _e;
}
pair<int,Point> operator &(const Line &b)const {
Point res = s;
if(sgn((s-e)^(b.s-b.e)) == 0) {
if(sgn((s-b.e)^(b.s-b.e)) == 0) return make_pair(0,res);
else return make_pair(1,res);
}
double t = ((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
res.x += (e.x-s.x)*t;
res.y += (e.y-s.y)*t;
return make_pair(2,res);
}
};
double dist(Point a,Point b) {
return sqrt((a-b)*(a-b));
}
bool inter(Line l1,Line l2) {
return
max(l1.s.x,l1.e.x) >= min(l2.s.x,l2.e.x) &&
max(l2.s.x,l2.e.x) >= min(l1.s.x,l1.e.x) &&
max(l1.s.y,l1.e.y) >= min(l2.s.y,l2.e.y) &&
max(l2.s.y,l2.e.y) >= min(l1.s.y,l1.e.y) &&
sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e)) <= 0 &&
sgn((l1.s-l2.e)^(l2.s-l2.e))*sgn((l1.e-l2.e)^(l2.s-l2.e)) <= 0;
}
bool Seg_inter_line(Line l1,Line l2) { //????l1?????l2??
return sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e)) <= 0;
}
Point upp[25], downp[25];
int main() {
int n;
while(~scanf("%d", &n), n) {
int i, j, k;
for(i = 0; i < n; i++) {
scanf("%lf %lf", &upp[i].x, &upp[i].y);
downp[i] = upp[i];
downp[i].y -= 1;
}
Line start(upp[0], downp[0]);
Line end(upp[n - 1], downp[n - 1]);
double ans = upp[0].x;
int isthrough = 1;
for(i = 0; i < n; i++) {
for(j = 0; j < n; j++) { //枚举所有上下折点
if(i == j) continue;
isthrough = 1;
int isin = 1;
Line l(upp[i], downp[j]);
for(k = 0; k < j; k++) { //看是否能从入口射入
Line tl(upp[k], downp[k]);
if(!Seg_inter_line(l, tl)) {
isin = 0;
isthrough = 0; //此处别忘清零。
break;
}
}
if(!isin) continue;
for(k = j + 1; k < n; k++) { //看能射到多远
Line tl(upp[k], downp[k]);
if(!Seg_inter_line(l, tl)) {
isthrough = 0;
pair<int, Point> o = (l & tl);
pair<int, Point> s = (start & l);
Line ttl;
if(o.second.y > upp[k].y) {
ttl = Line(upp[k], upp[k - 1]);
}
else {
ttl = Line(downp[k], downp[k - 1]);
}
pair<int, Point> e = (ttl & l);
ans = max(ans, e.second.x);
break;
}
}
if(isthrough) break;
}
if(isthrough) break;
}
if(isthrough) puts("Through all the pipe.");
else printf("%.2lf\n", ans);
}
return 0;
}