Gunman
Description
Consider a 3D scene with OXYZ coordinate system. Axis OX points to the right, axis OY points up, and axis OZ points away from you. There is a number of rectangular windows on the scene. The plane of each window is parallel to OXY , its sides are parallel to OX and OY . All windows are situated at different depths on the scene (different coordinates z > 0).
A gunman with a rifle moves along OX axis (y = 0 and z = 0). He can shoot a bullet in a straight line. His goal is to shoot a single bullet through all the windows. Just touching a window edge is enough. Your task is to determine how to make such shot. Input
The first line of the input file contains a single integer number n (2 <= n <= 100) — the number of windows on the scene. The following n lines describe the windows. Each line contains five integer numbers x
1i, y
1i, x
2i, y
2i, z
i (0 < x
1i, y
1i, x
2i, y
2i, z
i < 1000). Here (x
1i, y
1i, z
i) are coordinates of the bottom left corner of the window, and (x
2i, y
2i, z
i) are coordinates of the top right corner of the window (x
1i < x
2i, y
1i< y
2i). Windows are ordered by z coordinate (z
i > z
i-1 for 2 <= i <= n).
Output
Output a single word "UNSOLVABLE" if the gunman cannot reach the goal of shooting a bullet through all the windows.
Otherwise, on the first line output a word "SOLUTION". On the next line output x coordinate of the point from which the gunman must fire a bullet. On the following n lines output x, y, z coordinates of the points where the bullet goes through the consecutive windows. All coordinates in the output file must be printed with six digits after decimal point. Sample Input 3 1 3 5 5 3 1 2 5 7 5 5 2 7 6 6 Sample Output SOLUTION -1.000000 2.000000 3.000000 3.000000 4.000000 5.000000 5.000000 5.000000 6.000000 6.000000 Source |
提示
题意:
在三维坐标内有许多窗子,给出窗子大小以及位置,保证第一扇窗子要比第二扇远,一个枪手要想让子弹穿过全部窗子,请你为他在x轴选择一个合适的点从而实现他的目标。如果存在请输出“SOLUTION”,之后输出点的位置,接着输出子弹轨迹在窗子上的坐标,擦边也算穿过。不存在输出“UNSOLVABLE”。(答案不唯一)
思路:
三维问题转化为XOZ面和YOZ面问题。
先看XOZ面,假设所有窗子的y坐标为0,也就是投影,那么这就类似于POJ1039Pipe,运用模板枚举所有线段的段点,求出通过所有投影直线的方程。
之后看YOZ面,也是假设所有窗子的x坐标为0,这时直线的投影是通过原点的,根据y=kz来判断能否通过所有线段(窗子投影),比如y1是的下边,y2是窗子的上边,k1=y1/z,k2=y2/z,且利用k1,k2看直线有没有与线段相交。
如果两条都符合说明一定至少有1条直线成立,那么在XOZ面的直线与x轴相交的点就是答案,也可以求出直线与窗子相交的x值,在YOZ面求出的k值用来求出直线与窗子相交的y值,z值就是窗子的坐标。
示例程序
Source Code
Problem: 2165 Code Length: 2040B
Memory: 408K Time: 32MS
Language: GCC Result: Accepted
#include <stdio.h>
#include <math.h>
struct point
{
double x,y,z;
}p[100][2];
struct
{
double A,B,C; //直线方程是Ax+Bz+C=0
}line;
double k;
double cross(struct point a,struct point b,struct point o)
{
return (a.x-o.x)*(b.z-o.z)-(b.x-o.x)*(a.z-o.z);
}
int f(struct point a,struct point b,int n)
{
int i;
double t;
for(i=0;n>i;i++)
{
t=cross(p[i][0],b,a)*cross(p[i][1],b,a); //利用叉乘判断相交
if(t>=0&&fabs(t)>=1e-10)
{
return 0;
}
}
line.A=b.z-a.z;
line.B=a.x-b.x;
line.C=a.z*b.x-a.x*b.z;
return 1;
}
int check(int n)
{
int i,i1,i2;
for(i=0;n-1>i;i++)
{
for(i1=i+1;n>i1;i1++)
{
if(f(p[i][0],p[i1][0],n)==1)
{
return 1;
}
if(f(p[i][0],p[i1][1],n)==1)
{
return 1;
}
if(f(p[i][1],p[i1][0],n)==1)
{
return 1;
}
if(f(p[i][1],p[i1][1],n)==1)
{
return 1;
}
}
}
return 0;
}
int check1(int n)
{
int i,i1,i2;
double t;
for(i=0;n>i;i++)
{
for(i1=0;2>i1;i1++)
{
k=p[i][i1].y/p[i][0].z;
for(i2=0;n>i2;i2++)
{
if(k*p[i2][0].z-p[i2][1].y>1e-10||k*p[i2][0].z-p[i2][0].y<-1e-10) //判断是否相交
{
break;
}
}
if(n==i2)
{
return 1;
}
}
}
return 0;
}
int main()
{
int n,i;
scanf("%d",&n);
for(i=0;n>i;i++)
{
scanf("%lf %lf %lf %lf %lf",&p[i][0].x,&p[i][0].y,&p[i][1].x,&p[i][1].y,&p[i][0].z);
p[i][1].z=p[i][0].z;
}
if(check(n)==1&&check1(n)==1) //第一个是XOZ面的判断,第二个是YOZ面
{
printf("SOLUTION\n%.6f\n",-line.C/line.A);
for(i=0;n>i;i++)
{
printf("%.6f %.6f %.6f\n",-(p[i][0].z*line.B+line.C)/line.A,k*p[i][0].z,p[i][0].z);
}
}
else
{
printf("UNSOLVABLE");
}
return 0;
}