题目链接:http://poj.org/problem?id=1039
题意:给一个宽度为1的管子(不透明,不反射),这时有一段光线过来,问你最远可以射到哪里(直线),输出最远的x坐标,如果可以通过管子,就输出一段话
解析:假设这条光线不碰到任何顶点,那么你可以通过平移这条光线使得他碰到一个顶点,此时要射得远一点,碰到一个顶点后,发现你还可以通过旋转来使得他碰到第二个顶点,那么此时又会远一点,所以综上要想射得最远,那就必须经过管子的上下两个顶点,我的做法就是枚举上顶点和下顶点,然后维护最大值
注意坑点:坐标可能为负数,维护最大值要注意,判断直线与线段是否相交需要注意精度
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
struct point
{
double x,y;
}a[25],b[25];
struct line
{
point a,b;
line() {}
line(point _a,point _b)
{
a = _a;
b = _b;
}
};
double x_mul(point p0,point p1,point p2)
{
return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
int dblcmp(double x)
{
if(fabs(x)<1e-6)
return 0;
return x>0?1:-1;
}
bool judge(line l1,line l2)
{
double d1 = dblcmp(x_mul(l1.a,l1.b,l2.a));
double d2 = dblcmp(x_mul(l1.a,l1.b,l2.b));
return d1*d2<=0;
}
double solve(line l1,line l2)
{
double s1 = x_mul(l1.a,l1.b,l2.a);
double s2 = x_mul(l1.a,l1.b,l2.b);
double res = (s1*l2.b.x-s2*l2.a.x)/(s1-s2);
return res;
}
int main(void)
{
int n;
while(~scanf("%d",&n)&&n)
{
for(int i=0;i<n;i++)
{
scanf("%lf %lf",&a[i].x,&a[i].y);
b[i].x = a[i].x;
b[i].y = a[i].y-1.0;
}
double ans = -1e20;
int flag = 0;
for(int i=0;i<n&&!flag;i++)
{
for(int j=0;j<n;j++)
{
int k = 0;
for(;k<n;k++)
{
if(!judge(line(a[i],b[j]),line(a[k],b[k])))
break;
}
if(k==n)
{
flag = 1;
break;
}
if(k>max(i,j))
{
line t1 = line(a[i],b[j]);
line t2 = line(b[k-1],b[k]);
if(judge(t1,t2))
ans = max(ans,solve(t1,t2));
t2 = line(a[k-1],a[k]);
if(judge(t1,t2))
ans = max(ans,solve(t1,t2));
}
}
}
if(flag)
puts("Through all the pipe.");
else
printf("%.2f\n",ans);
}
return 0;
}