题目较难,在lrj书P359,
题意大致为:
有一宽度为1的折线管道,上面顶点为(xi,yi),所对应的下面顶点为(xi,yi-1),假设管道都是不透明的,不反射的,光线从左边入口处的(x1,y1),(x1,y1-1)之间射入,向四面八方传播,求解光线最远能传播到哪里(取x坐标)或者是否能穿透整个管道.
分析如下:上下顶点对于限制光线十分重要。首先:我们知道如果一条光线始终没有接触到一个顶点,那么它肯定不是最优的,可以通过平移使之接触到一个顶点,且更优化。其次,又可以推知,如果一条光线只接触到一个顶点,那么其也必定不是最优的,可以通过旋转使之接触两个顶点。且更优化。且这两个顶点必定是一个上顶点和一个下顶点。
分析到这里,思路就很明确了。 枚举任意上顶点和下顶点,构成一条直线。若该直线在x等于x0的情况下y值在down与up之间,则说明是一条”正确“的直线。那么接下来就是判断每段管道的上下层是否与该直线相交,直到规范相交为止,然后求出交点横坐标。最后求出每条正确直线的交点横坐标的最大值,即为题意要求的值。
下面是代码: 156K+47MS
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define eps 1e-3 //精度,实际可更大,最好为1e-6
#define inf 99999.0 //极大值,可更大
#define Max 30
#define Maxx(a,b) (a)>(b)?(a):(b) //注意宏最好单独使用,本题就是与<共同使用结果WA,混用的结果常常不可预知
typedef struct Point{ // 点坐标
double x;
double y;
}point;
int n;
point up[Max]; // 上方顶点
point down[Max]; // 下方顶点
int dblcmp(double p){ // 浮点数判断正负零模板
if(fabs(p)<eps)
return 0;
return p>0?1:-1;
}
double det(double x1,double y1,double x2,double y2){ // 叉积模板
return x1*y2-x2*y1;
}
double cross(point A,point B,point P){ // 判断p在直线AB左侧还是右侧,螺旋关系
return det(B.x-A.x,B.y-A.y,P.x-A.x,P.y-A.y);
}
bool check(point A,point B,point C,point D){ // 判断直线AB与线段CD是否非规范相交,若规范相交则要去掉=
return dblcmp(cross(A,B,C))*dblcmp(cross(A,B,D))<=0;
}
double intersection(point A,point B,point C,point D){ // 求直线AB与线段CD的交点
double area1=cross(A,B,C);
double area2=cross(A,B,D);
double c=dblcmp(area1),d=dblcmp(area2);
if(c*d<0) // 若为规范相交
return (area2*C.x-area1*D.x)/(area2-area1); // 交点横坐标
if(c*d==0){ // 若为非规范相交
if(c==0) // 若C在直线AB上,则交点坐标即为C的横坐标
return C.x;
else // 否则为D点横坐标
return D.x;
} // 若不相交,则返回负无穷,便于主程序一致处理
return -inf;
}
int main(){
while(scanf("%d",&n),n){
int i,j,k;
for(i=1;i<=n;i++){ // 输入管道坐标
scanf("%lf%lf",&up[i].x,&up[i].y);
down[i].x=up[i].x,down[i].y=up[i].y-1;
}
bool flag=false; // 标记是否为贯穿整个管道
double max_x=-inf; // 初始化最大横坐标为负无
for(i=1;i<=n;i++){
for(j=1;j<=n;j++){
if(i!=j){
for(k=1;k<=n;k++)
if(!check(up[i],down[j],up[k],down[k])) //检查规范相交的折线段
break;
if(k>n){ // 若无规范相交,则说明贯穿整个管道
flag=true; //置标记为true
break; // 跳出j循环
}
int what=Maxx(i,j); //取i,j中的最大值
if(k>what){ // 若k小于或等于i与j中的最大值,则说明该直线不是正确的直线,否则为正确直线
double temp=intersection(up[i],down[j],up[k],up[k-1]); //因无法判断是上面还是下面规范相交,故可取最大值,不影响结果
if(max_x<temp) //取大值
max_x=temp;
temp=intersection(up[i],down[j],down[k],down[k-1]); //假设与下面管道相交
if(max_x<temp) // 取大值
max_x=temp;
}
}
}
if(flag) // 若贯穿整个管道,则跳出i循环
break;
}
if(flag) // 若贯穿整个管道
printf("Through all the pipe.\n");
else // 否则取最大横坐标
printf("%.2lf\n",max_x);
}
return 0;
}