HDU:6697-Closest Pair of Segments(空间内的最近线段)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6697

题意:现在在平面上有 n n n条线段,并且定义线段 x x x和线段 y y y的最短距离为在 x x x上任取一点和 y y y上任取一点,最小的两点之间距离为两线段的最短距离。

解题心得:

  • 先分析复杂度, n n n的范围是 10000 10000 10000,如果直接 n 2 n^2 n2暴力加上 T T T组数据肯定会超时。这个时候可以按线段的左端点的 x x x排序,这样在按顺序枚举任意两条直线的时候如果右侧线段左端点 x x x左侧线段右端点 x x x的差值大于目前找到的答案,这个时候可以跳出第二重循环。就是暴力的时候剪枝。
  • 然后就是两个线段怎么求最小距离,设现在一个三维空间内有四个点,分别是 A B C D ABCD ABCD,坐标分别是 A ( x 1 , y 1 , z 1 ) A(x1, y1, z1) A(x1,y1,z1) B ( x 2 , y 2 , z 2 ) B(x2, y2, z2) B(x2,y2,z2) C ( x 3 , y 3 , z 3 ) C(x3, y3, z3) C(x3,y3,z3) D ( x 4 , y 4 , z 4 ) D(x4, y4, z4) D(x4,y4,z4),两条线段分别是 A B AB AB C D CD CD。设 P , Q P,Q P,Q分别为在 A B AB AB C D CD CD上的点, P , Q P,Q P,Q可以用参数方程表示, P P P可以表示为: { X = x 1 + S ( x 2 − x 1 ) Y = y 1 + S ( y 2 − y 1 ) Z = z 1 + S ( z 2 − z 1 ) \left\{\begin{matrix} & X=x1+S(x2-x1) & \\ & Y=y1+S(y2-y1) & \\ & Z=z1+S(z2-z1) & \end{matrix}\right. X=x1+S(x2x1)Y=y1+S(y2y1)Z=z1+S(z2z1) Q Q Q可以表示为: { U = x 3 + T ( x 4 − x 3 ) V = y 3 + T ( y 4 − y 3 ) W = z 3 + T ( z 4 − z 3 ) \left\{\begin{matrix} & U=x3+T(x4-x3) & \\ & V=y3+T(y4-y3) & \\ & W=z3+T(z4-z3) & \end{matrix}\right. U=x3+T(x4x3)V=y3+T(y4y3)W=z3+T(z4z3)
    参数T和S必须满足 0 &lt; T &lt; 1 0&lt;T&lt;1 0<T<1 0 &lt; S &lt; 1 0&lt;S&lt;1 0<S<1这个时候 P , Q P,Q P,Q两点才在线段上,否则是在线段的延长线上。两点间的距离 P Q = ( X − U ) 2 + ( Y − V ) 2 + ( Z − W ) 2 PQ=\sqrt{(X-U)^2+(Y-V)^2+(Z-W)^2} PQ=(XU)2+(YV)2+ZW)2 ,设距离的平方为 f ( s , t ) = [ ( x 1 − x 3 ) + S ( x 2 − x 1 ) − T ( x 4 − x 3 ) ] 2 + [ ( y 1 − y 3 ) + S ( y 2 − y 1 ) − T ( y 4 − y 3 ) ] 2 + [ ( z 1 − z 3 ) + S ( x 2 − x 1 ) + T ( z 4 − z 3 ) ] 2 f(s,t)=[(x1-x3)+S(x2-x1)-T(x4-x3)]^2+[(y1-y3)+S(y2-y1)-T(y4-y3)]^2+[(z1-z3)+S(x2-x1)+T(z4-z3)]^2 f(s,t)=[(x1x3)+S(x2x1)T(x4x3)]2+[(y1y3)+S(y2y1)T(y4y3)]2+[(z1z3)+S(x2x1)+T(z4z3)]2
    这个时候我们需要得到的是 f ( s , t ) f(s,t) f(s,t)的最小值,所以对 f ( s , t ) f(s,t) f(s,t)分别求对 s s s t t t的偏导数,并且令偏导数为 0 0 0,联立可以可以得到 { [ ( x 2 − x 1 ) 2 + ( y 2 − y 1 ) 2 + ( z 2 − z 1 ) 2 ] S − [ ( x 2 − x 1 ) ( x 4 − x 3 ) + ( y 2 − y 1 ) ( y 4 − y 3 ) + ( z 2 − z 1 ) ( z 4 − z 3 ) ] T = ( x 1 − x 2 ) ( x 1 − x 3 ) + ( y 1 − y 2 ) ( y 1 − y 3 ) + ( z 1 − z 2 ) ( z 1 − z 3 ) − [ ( x 2 − x 1 ) ( x 4 − x 3 ) + ( y 2 − y 1 ) ( y 4 − y 3 ) + ( z 2 − z 1 ) ( z 4 − z 3 ) ] S + [ ( x 4 − x 3 ) 2 + ( y 4 − y 3 ) 2 + ( z 4 − z 3 ) 2 ] T = ( x 1 − x 3 ) ( x 4 − x 3 ) + ( y 1 − y 3 ) ( y 4 − y 3 ) + ( z 1 − z 3 ) ( z 4 − z 3 ) \left\{\begin{matrix} &amp; [(x2-x1)^2+(y2-y1)^2+(z2-z1)^2]S-[(x2-x1)(x4-x3)+(y2-y1)(y4-y3)+(z2-z1)(z4-z3)]T=(x1-x2)(x1-x3)+(y1-y2)(y1-y3)+(z1-z2)(z1-z3) &amp; \\ &amp; -[(x2-x1)(x4-x3)+(y2-y1)(y4-y3)+(z2-z1)(z4-z3)]S+[(x4-x3)^2+(y4-y3)^2+(z4-z3)^2]T=(x1-x3)(x4-x3)+(y1-y3)(y4-y3)+(z1-z3)(z4-z3) &amp; \end{matrix}\right. {[(x2x1)2+(y2y1)2+(z2z1)2]S[(x2x1)(x4x3)+(y2y1)(y4y3)+(z2z1)(z4z3)]T=(x1x2)(x1x3)+(y1y2)(y1y3)+(z1z2)(z1z3)[(x2x1)(x4x3)+(y2y1)(y4y3)+(z2z1)(z4z3)]S+[(x4x3)2+(y4y3)2+(z4z3)2]T=(x1x3)(x4x3)+(y1y3)(y4y3)+(z1z3)(z4z3)
    对于上式进行求解可以得到 S S S T T T的值,这个时候若 S S S T T T都满足 ( 0 , 1 ) (0, 1) (0,1)区间则可以直接得到 P , Q P,Q P,Q两点具体的值,这样就得到了 P Q PQ PQ的距离。
    S , T S,T S,T的范围不满足条件,则可以先得到 A , B A,B A,B两点到线段 C D CD CD的距离 d i s 1 , d i s 2 dis1,dis2 dis1,dis2 C , D C,D C,D两点到线段 A B AB AB的距离 d i s 3 dis3 dis3 d i s 4 dis4 dis4,这时两线段的距离就是 m i n ( d i s 1 , d i s 2 , d i s 3 , d i s 4 ) min(dis1, dis2, dis3, dis4) min(dis1,dis2,dis3,dis4)


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5+100;

struct Seg {
    double x1, x2, y1, y2;

    bool operator < (const Seg&x) const {
        return x1 < x.x1;
    }
}seg[maxn];

int t, n;
double ans;

void init() {
    ans = 2e18;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%lf%lf%lf%lf", &seg[i].x1, &seg[i].y1, &seg[i].x2, &seg[i].y2);
        if(seg[i].x2 < seg[i].x1) {
            swap(seg[i].x1, seg[i].x2);
            swap(seg[i].y1, seg[i].y2);
        }
    }
    sort(seg+1, seg+1+n);
}

bool checke(int x, int y) {
    double dis1 = seg[x].x2;
    double dis2 = seg[y].x1;

    if(dis2 - dis1 > ans) return true;
    return false;
}

double PointToSegDist(double x, double y, double x1, double y1, double x2, double y2)//点到直线距离
{
    double cross = (x2 - x1) * (x - x1) + (y2 - y1) * (y - y1);
    if (cross <= 0) return sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1));

    double d2 = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
    if (cross >= d2) return sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));

    double r = cross / d2;
    double px = x1 + (x2 - x1) * r;
    double py = y1 + (y2 - y1) * r;
    return sqrt((x - px) * (x - px) + (py - y) * (py - y));
}

double get_dis(int x, int y) {
    double k1, k2, k3, k4, a1, a2;
    double x1 = seg[x].x1, x2 = seg[x].x2, y1 = seg[x].y1, y2 = seg[x].y2;
    double x3 = seg[y].x1, x4 = seg[y].x2, y3 = seg[y].y1, y4 = seg[y].y2;
    double z2 = 0, z1 = 0, z4 = 0, z3 = 0;//二维平面则z坐标没有使用

    k1 = ((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) + (z2-z1)*(z2-z1));
    k2 = -((x2-x1)*(x4-x3) + (y2-y1)*(y4-y3) + (z2-z1)*(z4-z3));
    a1 = (x1-x2)*(x1-x3) + (y1-y2)*(y1-y3) + (z1-z2)*(z1-z3);

    k3 = -((x2-x1)*(x4-x3) + (y2-y1)*(y4-y3) + (z2-z1)*(z4-z3));
    k4 = (x4-x3)*(x4-x3) + (y4-y3)*(y4-y3) + (z4-z3)*(z4-z3);
    a2 = (x1-x3)*(x4-x3) + (y1-y3)*(y4-y3) + (z1-z3)*(z4-z3);

    double temp_k = k1;
    k1 *= k3;
    k2 *= k3;
    a1 *= k3;

    k3 *= temp_k;
    k4 *= temp_k;
    a2 *= temp_k;

    double T = (a1-a2)/(k2-k4);
    double S = (a1-k2*T)/k1;

    if(S >=0 && S <= 1.0 && T >= 0 && T <= 1.0) {
        double X = x1 + (x2-x1)*S;
        double Y = y1 + (y2-y1)*S;
        double Z = z1 + (z2-z1)*S;

        double X1 = x3 + (x4-x3)*T;
        double Y1 = y3 + (y4-y3)*T;
        double Z1 = z3 + (z4-z3)*T;

        return sqrt((X-X1)*(X-X1) + (Y-Y1)*(Y-Y1) + (Z-Z1)*(Z-Z1));
    } else {
        double dis1 = PointToSegDist(x1, y1, x3, y3, x4, y4);
        double dis2 = PointToSegDist(x2, y2, x3, y3, x4, y4);
        double dis3 = PointToSegDist(x3, y3, x1, y1, x2, y2);
        double dis4 = PointToSegDist(x4, y4, x1, y1, x2, y2);

        return min(min(dis1, dis2), min(dis3, dis4));
    }
}

void solve() {
    for(int i=1;i<=n;i++) {
        for(int j=i+1;j<=n;j++) {
            if(checke(i, j)) break;
            else ans = min(ans, get_dis(i, j));
        }
    }
}

int main() {
//    freopen("1.in.txt", "r", stdin);
    scanf("%d", &t);
    while(t--) {
        init();
        solve();
        printf("%.9f\n", ans);
    }
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值