AT_abc426_e题解

传送门

传送门

题面

高桥和青木在一个二维直角坐标系上沿直线从\((S_x,S_y)\)\((T_x,T_y)\)走动,速度都为1,当一个人到达了他的终点,那么他会停在那里,直到两人都走完。

那么什么时候两人直线距离最短。请求出这个最短的距离。

思路

分开成两段,一段是路线短的那个人走完且两人走的距离相同(没走完也停下);另一段是路线短的那个人停在终点,等路线长的人走完。
可以用三分求出什么时候直线距离最短。

具体详细步骤

首先我们需要一些结构体,point(存储一个点的横纵坐标)和line(存储两个点,两点一线)

struct point
{
    double x, y; // 横纵坐标
};
struct line
{
    point s, e; // 起始点和终点
};

接着我们要这几个函数:

1、distance(后简称dis)函数,求出两个点之间的距离,直接利用勾股定理即可

double dis(point& p1, point& p2)
{
    double t1 = p1.x - p2.x; // 纵向距离
    double t2 = p1.y - p2.y; // 横向距离
    return sqrt(t1 * t1 + t2 * t2); // 勾股定理求最短距离
}

2、position after moving(后简称pam)函数,求出从一个人沿着他的路线走\(t\)秒后到达的位置。可以用比例来做。

point pam(line& L, double t) 
{
    double X = L.e.x - L.s.x; // 纵向距离
    double Y = L.e.y - L.s.y; // 横向距离
    double len = dis(L.s, L.e); // 直线距离
    double r = t / len; // t占的比例
    r = min(r, 1.0); // 到达终点就停下,所以不会超过终点。
    return { L.s.x + r * X, L.s.y + r * Y }; // 按照比例分别求出位置的行与列。
}

3、distance after moving(后简称dam)函数,求出两个人分别沿着他们的路径走\(t\)秒后的直线距离,可以借用dis和pam函数。找到两人的位置然后求距离。

double dam(line& L1, line& L2, double t)
{
    point p1 = pam(L1, t); // 一个人到达的位置
    point p2 = pam(L2, t); // 另一人到达的位置
    return dis(p1, p2); // 直线距离
}

4、closest moment(后简称cm) 函数,用三分法求出它们的距离何时最短。
求法:
由于我们已经将问题分成两段。可以看出每一段他们的距离随着时间变化一定是个谷型(即dam的函数图像),三分法是如何求出谷底的呢?

最开始\(L=0,R=路线长度\)
每次我们在谷上在\(L\)\(R\)中随意找到两个位置,记为\(mid1,mid2 (mid1 < mid2)\),当\(dam(L1,L2,mid1) < dam(L1,L2,mid2)\)时,可以发现答案只可能在\(mid1\)\(R\)中间。而当\(dam(L1,L2,mid1) > dam(L1,L2,mid2)\)时,可以发现答案只可能在\(L\)\(mid2\)中间。(可以自己画一下)

我们可以每次让\(mid1 = (L+L+R)/3,mid2 = (L+R+R)/3\)

double cm(line& L1, line& L2)
{
    double len = max(dis(L1.s, L1.e), dis(L2.s, L2.e)); // 路径长度(注意由于第二段两个长度是不同的,取max)
    double L = 0, R = len;
    for (int i = 1; i <= 50; i++) // 多做几次不易错
    {
        double mid1 = (L + L + R) / 3; // 找到mid1
        double mid2 = (L + R + R) / 3; // 找到mid2
        if (dam(L1, L2, mid1) < dam(L1, L2, mid2)) // 谷底在左边
            R = mid2;
        else // 谷底在右边
            L = mid1;
    }
    return L;
}

5、最后一个,解题solve函数,详情看注释

void solve()
{
    line L1, L2;
    cin >> L1.s.x >> L1.s.y >> L1.e.x >> L1.e.y; // 输入第一个人的路线
    cin >> L2.s.x >> L2.s.y >> L2.e.x >> L2.e.y; // 输入第二个人的路线
    double mnlen = min(dis(L1.s, L1.e), dis(L2.s, L2.e)); // 找到较短的路线长度 
    line k1, k2, k3, k4; // 分成两次,每次两人,一共四段
    // 分成两段,第一段时双方都走mnlen的时间,其中一人走完,一人没有走完。然后第二段一人不动,一人走完剩下的。
    k1.s = L1.s; k1.e = pam(L1, mnlen); // 第一段第一个人。
    k2.s = L2.s; k2.e = pam(L2, mnlen); // 第一段第二个人。
    k3.s = k1.e; k3.e = L1.e; // 第二段第一个人
    k4.s = k2.e; k4.e = L2.e; // 第二段第二个人
    double ans1 = dam(k1, k2, cm(k1, k2)); // 找到第一段的最近距离
    double ans2 = dam(k3, k4, cm(k3, k4)); // 找到第二段的最近距离
    printf("%.8f\n", min(ans1, ans2)); // 比一下,看哪个最近。
}

6、main主函数:可以加个快读(1946ms -> 865ms)输入\(T\),运行\(T\)次solve函数,然后return 0;

int main()
{
    ios::sync_with_stdio(false); // 快读
    cin.tie(0); // 快读
    int T; // 数据组数
    cin >> T; 
    while (T--) 
    {
        solve();
    }
    return 0;
}

代码

高清代码附上:

#include <bits/stdc++.h>
using namespace std;
const int N = 50;
struct point
{
    double x, y;
};
struct line
{
    point s, e;
};
double dis(point& p1, point& p2)
{
    double t1 = p1.x - p2.x;
    double t2 = p1.y - p2.y;
    return sqrt(t1 * t1 + t2 * t2);
}
point pam(line& L, double t)
{
    double X = L.e.x - L.s.x;
    double Y = L.e.y - L.s.y;
    double len = dis(L.s, L.e);
    double r = t / len;
    r = min(r, 1.0);
    return { L.s.x + r * X, L.s.y + r * Y };
}
double dam(line& L1, line& L2, double t)
{
    point p1 = pam(L1, t);
    point p2 = pam(L2, t);
    return dis(p1, p2);
}
double cm(line& L1, line& L2)
{
    double len = max(dis(L1.s, L1.e), dis(L2.s, L2.e));
    double L = 0, R = len;
    for (int i = 1; i <= N; i++)
    {
        double mid1 = (L + L + R) / 3;
        double mid2 = (L + R + R) / 3;
        if (dam(L1, L2, mid1) < dam(L1, L2, mid2))
            R = mid2;
        else
            L = mid1;
    }
    return L;
}
void solve()
{
    line L1, L2;
    cin >> L1.s.x >> L1.s.y >> L1.e.x >> L1.e.y;
    cin >> L2.s.x >> L2.s.y >> L2.e.x >> L2.e.y;
    double mnlen = min(dis(L1.s, L1.e), dis(L2.s, L2.e));
    line k1, k2, k3, k4;
    k1.s = L1.s; k1.e = pam(L1, mnlen);
    k2.s = L2.s; k2.e = pam(L2, mnlen);
    k3.s = k1.e; k3.e = L1.e;
    k4.s = k2.e; k4.e = L2.e;
    double ans1 = dam(k1, k2, cm(k1, k2));
    double ans2 = dam(k3, k4, cm(k3, k4));
    printf("%.8f\n", min(ans1, ans2));
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T;
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

完结撒花~~~~~~~

posted @ 2025-10-12 01:06  MichaelZeng  阅读(9)  评论(0)    收藏  举报