hdu 4741 Save Labman No.004

题目大意:求空间两条直线的最短距离及最近的点坐标


解题思路:http://wenku.baidu.com/view/ea02ff8a6529647d2728520c.html



代码实现1:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
struct node
{
    double x;
    double y;
    double z;
}p[10], P, Q;
long double s, t;
long double pf(long double a, long double b)
{
     return (a - b) * (a - b);
}
long double dis(node a, node b)
{
     return sqrt(pf(a.x, b.x) + pf(a.y, b.y) + pf(a.z, b.z));
}
void Cal()
{
     long double t1 = pf(p[2].x, p[1].x) + pf(p[2].y, p[1].y) + pf(p[2].z ,p[1].z);
     long double t2 = (p[2].x - p[1].x) * (p[4].x - p[3].x) + (p[2].y - p[1].y) * (p[4].y - p[3].y)
                 + (p[2].z - p[1].z) * (p[4].z - p[3].z);
     long double t3 = (p[1].x - p[2].x) * (p[1].x - p[3].x) + (p[1].y - p[2].y) * (p[1].y - p[3].y)
                 + (p[1].z - p[2].z) * (p[1].z - p[3].z);

     long double t4 = pf(p[4].x, p[3].x) + pf(p[4].y, p[3].y) + pf(p[4].z ,p[3].z);
     long double t5 = (p[1].x - p[3].x) * (p[4].x - p[3].x) + (p[1].y - p[3].y) * (p[4].y - p[3].y)
                 + (p[1].z - p[3].z) * (p[4].z - p[3].z);
     t = (t1 * t5 + t2 * t3) / (t1 * t4 - t2 * t2);
     s = (t2 * t5 + t3 * t4) / (t1 * t4 - t2 * t2);

}
int main()
{
    int t0;
    scanf("%d", &t0);
    for(int i = 1; i <= t0; i++)
    {
            for(int j = 1; j <= 4; j++)
            {
                    scanf("%lf%lf%lf", &p[j].x, &p[j].y, &p[j].z);
            }
            Cal();
            P.x = p[1].x + s * (p[2].x - p[1].x);
            P.y = p[1].y + s * (p[2].y - p[1].y);
            P.z = p[1].z + s * (p[2].z - p[1].z);

            Q.x = p[3].x + t * (p[4].x - p[3].x);
            Q.y = p[3].y + t * (p[4].y - p[3].y);
            Q.z = p[3].z + t * (p[4].z - p[3].z);

            long double ans = dis(P, Q);
            printf("%.6lf\n", (double)ans);
            printf("%.6lf %.6lf %.6lf %.6lf %.6lf %.6lf\n",(double)P.x, (double)P.y, (double)P.z, (double)Q.x, (double)Q.y, (double)Q.z);

    }
    return 0;
}

http://blog.sina.com.cn/s/blog_a401a1ea0101ij9z.html


容易理解的常规方法:

已知空间中两线段,如果它们无限变粗,判断是否相交。(主要讨论不在同一平面的情况)

线段AB 线段CD

问题的关键是求出这两条任意直线之间的最短距离,以及在这个距离上的两线最接近点坐标,判断该点是否在线段AB线段CD上。

首先将直线方程化为对称式,得到其方向向量n1=(a1,b1,c1),n2=(a2,b2,c2).

再将两向量叉乘得到其公垂向量N=(x,y,z),在两直线上分别选取点A,B(任意),得到向量AB,

求向量AB在向量N方向的投影即为两异面直线间的距离了(就是最短距离啦)。

最短距离的求法:d=|向量N*向量AB|/|向量N|(上面是两向量的数量积,下面是取模)。

设交点为C,D,带入公垂线N的对称式中,又因为C,D两点分别满足一开始的直线方程,所以得到关于C(或D)的两个连等方程,分别解出来就好了!

没有理解的简单方法:



三维向量的叉积公式:

向量a×向量b=| i j k| |a1 b1 c1| |a2 b2 c2|
这是一个三阶行列式
其值为 (b1c2-b2c1, c1a2-a1c2, a1b2-a2b1)
(i、j、k分别为空间中相互垂直的三条坐标轴的单位向量)。
代码实现2:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

struct Point
{
     double x, y, z;
     Point(double x = 0, double  y = 0, double z = 0):x(x), y(y), z(z){}
};

typedef Point Vector;

Vector operator + (Vector a, Vector b){return Vector(a.x + b.x, a.y + b.y, a.z + b.z);};
Vector operator - (Vector a, Vector b){return Vector(a.x - b.x, a.y - b.y, a.z - b.z);};
Vector operator * (Vector a, double b){return Vector(a.x * b, a.y * b, a.z * b);};
Vector operator / (Vector a, double b){return Vector(a.x / b, a.y / b, a.z / b);};

Vector Cross(Vector a, Vector b)
{
     return Vector(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
}

double Dot(Vector a, Vector b)
{
     return (a.x * b.x + a.y * b.y + a.z * b.z);
}
double Getlen(Vector a)
{
      return sqrt(a.x*a.x + a.y*a.y + a.z*a.z);
}
int  main()
{
     Point a1, b1, a2, b2;
     int n;
     scanf("%d", &n);
     for(int i = 0; i < n; i ++)
     {
             scanf("%lf %lf %lf", &a1.x, &a1.y, &a1.z);
             scanf("%lf %lf %lf", &b1.x, &b1.y, &b1.z);
             scanf("%lf %lf %lf", &a2.x, &a2.y, &a2.z);
             scanf("%lf %lf %lf", &b2.x, &b2.y, &b2.z);

             Vector n1 = (a1 - b1);
             Vector n2 = (a2 - b2);

             Vector n = Cross(n1, n2);

             Vector ab = (a1 - a2);
             double ans = Dot(n , ab) / Getlen(n);

             Point p1 = a1, p2 = a2;
             Vector d1 = (b1 - a1), d2 = (b2 - a2);

             double t1 = Dot( Cross(p2 - p1, d2), Cross(d1, d2));
             double t2 = Dot( Cross(p2 - p1, d1) , Cross(d1, d2));
             double fm = Getlen(Cross(d1, d2));
             t1 = t1 / (fm * fm);
             t2 = t2 / (fm * fm);

             Point p, q;
             p = (a1 + (b1 - a1) * t1);
             q = (a2 + (b2 - a2) * t2);

             printf("%.6f\n", fabs(ans));
             printf("%.6f %.6f %.6f ",p.x, p.y, p.z);
             printf("%.6f %.6f %.6f\n",q.x, q.y, q.z);


     }
     return 0;
}


有一点我好想吐槽:为什么一定要用long double 才能过,,,,郁闷无极限啊


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值