sgu110-計算幾何

110. Dungeon

time limit per test: 0.25 sec. 
memory limit per test: 4096 KB

The mission of space explorers found on planet M the vast dungeon. One of the dungeon halls is fill with the bright spheres. The explorers find out that the light rays reflect from the surface of the spheres according the ordinary law (the incidence angle is equal to the reflectance angle, the incidence ray, the reflected ray and the perpendicular to the sphere surface lay in the one plane). The ancient legend says that if the light ray will reflect from the spheres in the proper order, than the door to the room with very precious ancient knowledge will open. You are not to guess the right sequence; your task is much simpler. You are given the positions and the radii of the spheres, the place where the laser shot was made and the direction of light propagation. And you must find out the sequence in which the light will be reflected from the spheres.

Input

The first line of input contains the single integer n (1≤n≤50) - the amount of the spheres. The next n lines contain the coordinates and the radii of the spheres xi, yi, zi, ri (the integer numbers less or equal to 10000 by absolute value). The last line contains 6 real numbers - the coordinates of two points. The first one gives the coordinates of the place of laser shot, and the second gives the direction in which it was made (the second point is the point on the ray). The starting point of the ray lies strictly outside of any sphere.

Output

Your program must output the sequence of sphere numbers (spheres are numbers from 1 as they was given in input), from which the light ray was reflected. If the ray will reflect more the 10 times, than you must output first 10, then a space and the word 'etc.' (without quotes). Notice: if the light ray goes at a tangent to the sphere you must assume that the ray was reflected by the sphere.

Sample Input 1

1 
0 0 2 1 
0 0 0 0 0 1

Sample Output 1

1

Sample Input 2

2 
0 0 2 1 
0 0 -2 1 
0 0 0 0 0 100

Sample Output 2

1 2 1 2 1 2 1 2 1 2 etc.

題意:

宇宙探險家們在一次任務中發現了M行星上的一個巨大的地牢,地牢的一個大廳中充滿了閃閃發光的小球。探險家們發現光線在小球表面反射時遵循光的鏡面反射規律(入射角=反射角)。根據古老的傳說,如果將光線以恰當的順序在小球之間反射,那麼通道一間隱藏着古老的珍貴知識的房間的門就會開啓。放心,你的任務不是去猜這個恰當的順序是什麼。你的任務簡單得多,你被告知小球的位置和半徑,以及入射光線的光源位置和方向,然後你要找出光線在小球上反射的序列。

輸入

第一行包含一個整數 n (1≤n≤50) 代表小球總數。 接下來 n 行包括小球的座標和半徑xi, yi, zi, ri (這些整數的絕對值不超過10000)。最後一行有6個實數 - 兩個點的座標。第一個點是入射點。第二個點表示光線的方向 (光線從第一個點直接指向第二個點)。光源不會在任何小球內部。

輸出

你的程序必須輸出小球編號組成的序列 (小球從1起按照輸入的順序編號),也就是光反射的順序。如果光反射了超過10次,你只需要輸出前10個再加上 'etc.' (引號不算)。 注意:如果光以切線方向射向小球,你也要認爲他在球上反射了。

這裏要注意的是最後一行輸出,我以前第一個點是start point,第二個點是方向,沒想到第二個點也是線上的,方向是point2-point1,我在這裏debug2,3個小時......坑爹啊

我就說幾個注意點吧

1.根據直線的參數方程,我們可以很容易把交點算出來

2.要注意精度問題,相切的時候也算反射,這時候b^2-4ac = 0在double的時候是不成立的,你可以設置一個1e-100

3.如何計算反射向量呢?(T_T 居然不給上傳圖片...)

我喜歡用在法線上取一點M,使得SM(start point)垂直法線

然後start point 關於M的對稱點就是另一個點

4.如果光反射了超過10次,你只需要輸出前10個再加上 'etc.' 如果恰好10次則不需要.

5.注意如果我上一次是1,那麼這次計算的時候要排除1

TEST
3
0 0 2 1
0 0 5 1
0 0 8 1
0 1 0 0 1 1
ANSWER
1 2 3

TEST
3
1 0 2 1
1 0 3 1
1 0 4 1
0 0 0 0 0 1
ANSWER
1 2 3

my accept code

#include <iostream>
#include <cmath>
using namespace std;

#define ZERO 1e-10

inline double sqr(double x){return x*x;}
inline double min(double x1, double x2){return x1>x2 ? x2:x1;}

typedef struct _vector3f
{
	double x;
	double y;
	double z;
	_vector3f(){};
	_vector3f(double _x, double _y, double _z){x = _x;y = _y;z = _z;}
} vector3f;

typedef struct _sphere
{
	double x;
	double y;
	double z;
	double r;
	_sphere(){};
	_sphere(double _x, double _y, double _z, double _r){x = _x;y = _y;z = _z;r = _r;}
} Sphere;

/*
 * -1 means no root
 *  1 means we have root
 */
int calIntersaction(vector3f start, vector3f direction, Sphere sphere, double &t)
{
	double a = sqr(direction.x) + sqr(direction.y) + sqr(direction.z);

	double t1 = start.x - sphere.x;
	double t2 = start.y - sphere.y;
	double t3 = start.z - sphere.z;

	double b = 2.0 * (t1*direction.x + t2*direction.y + t3*direction.z);
	double c = sqr(t1) + sqr(t2) + sqr(t3) - sqr(sphere.r);

	double delta = sqr(b) - 4.0*a*c;
	//cout << "delta = " << delta << endl;
	if(delta < -ZERO)
		return -1;
	double root1 = ((-1.0 * b) + sqrt(delta)) / (2.0 * a);
    double root2 = ((-1.0 * b) - sqrt(delta)) / (2.0 * a);
    if(root1 < -ZERO)
    	return -1;
    
    t = min(root1, root2);
    return 1;
}
	
/*
 * center is the center of sphere
 * p is the intersection
 * incident is the incident ray
 */
vector3f calRefVector(Sphere center, vector3f p, vector3f start)
{
	vector3f normal = vector3f(p.x-center.x, p.y-center.y, p.z-center.z);
	
	//vector n1 = (p.x+t*n.x-p1.x, p.y+t*n.y-p1.y, p.z+t*n.z-p1.z) 
	//vector n2 = normal
	// n1*n2 = 0	
	double dx = p.x-start.x;
	double dy = p.y-start.y;
	double dz = p.z-start.z;
	
	double right = dx*normal.x + dy*normal.y + dz*normal.z;
	double left = sqr(normal.x) + sqr(normal.y) + sqr(normal.z);
	double t = -1.0*right/left;
	
	vector3f middle = vector3f(p.x+t*normal.x, p.y+t*normal.y, p.z+t*normal.z);
	vector3f anotherPoint = vector3f(2.0*middle.x-start.x, 2.0*middle.y-start.y, 2.0*middle.z-start.z);
	return vector3f(anotherPoint.x-p.x, anotherPoint.y-p.y, anotherPoint.z-p.z);
}

int main()
{
	int N;
	double x,y,z,r;
	Sphere myspheres[50];
	vector3f start,direction;
	cin >> N;
	for(int i = 0;i < N;i++){
		cin >> x >> y >> z >> r;
		myspheres[i] = Sphere(x,y,z,r);
	}
	cin >> x >> y >> z;
	start = vector3f(x,y,z);
	cin >> x >> y >> z;
	direction = vector3f(x - start.x, y-start.y, z-start.z);
	
	int lastNumber = -1;
	int i;
	for(i = 0;i < 11;i++){
		//find the nearest
		int reflectNumber = -1;
		double minium = 1.79769313486231570E+308;
		for(int k = 0;k < N;k++){
			double t;
			int result = calIntersaction(start, direction, myspheres[k], t);
			if((result != -1) && (lastNumber != k)){
				if(minium > t){
					minium = t;
					reflectNumber = k;
				}
			}
		}
		lastNumber = reflectNumber;
		
		if(reflectNumber != -1){
			if(i == 10){
				cout << "etc.";
				break;
			}
			cout << reflectNumber+1 << " ";
			vector3f intersection = vector3f(start.x+minium*direction.x, start.y+minium*direction.y, start.z+minium*direction.z);
			//update start , direction
			direction = calRefVector(myspheres[reflectNumber], intersection, start);
			start.x = intersection.x;
			start.y = intersection.y;
			start.z = intersection.z;
		}else{
			break;
		}
		
	}
	cout << endl;
	return 0;
}


發佈了63 篇原創文章 · 獲贊 15 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章