题解:P1027 [NOIP 2001 提高组] Car 的旅行路线

· · 题解

这道题硬控我 1 小时。。。

题目难点:

主要是输入只告诉了我们三个点的坐标,所以需要求第四个点的坐标,要是每种情况都讨论一边的话代码比较长(但也能做),所以推荐写循环。剩下的就是最短路了,这里用 Floyd 就行了。

求第四点的方法:

初中数学中我们学到了两点间距离公式,中点公式和勾股定理,运用这三点,就能求出第四点的坐标了。
代码实现:

void work(int xx) {
    int ap, bp, cp; //cp代表直角三角形中斜边所对应的点,ap和bp代表另外两点
    for(int i = 0; i <= 2; i++) {
        for(int j = 0; j <= 2; j++) {
            for(int l = 0; l <= 2; l++) {
                if(i == j || j == l || i == l) continue;
                double ka = dis2(a[xx].x[i], a[xx].y[i], a[xx].x[j], a[xx].y[j]);
                double kb = dis2(a[xx].x[j], a[xx].y[j], a[xx].x[l], a[xx].y[l]);
                double kc = dis2(a[xx].x[i], a[xx].y[i], a[xx].x[l], a[xx].y[l]);
                if(ka + kb == kc) ap = l, bp = i, cp = j; //勾股定理判断是否构成直角三角形
                else continue;
            } 
        }
    }
    double midx = (a[xx].x[ap] + a[xx].x[bp]) / 2.0, midy = (a[xx].y[ap] + a[xx].y[bp]) / 2.0;
    midx *= 2, midy *= 2;
    newx = midx - a[xx].x[cp], newy = midy - a[xx].y[cp];
    return;
}

最重要的一点,这里求两点间距离的 dis2 函数千万不要开根号,不然会有精度损失,导致没有答案。

``` double dis2(double a1, double b1, double a2, double b2) { return (a1 - a2) * (a1 - a2) * 1.0 + (b1 - b2) * (b1 - b2) * 1.0; } ``` ### 还有一个技巧: 除了 $a$ 数组存每一个城市的机场坐标以及里程费以外,再开一个数组 $b$,保存每个机场的坐标以及从属的城市编号,这使后续操作方便许多。 就是这样: ``` struct Node { double x[5], y[5], t; } a[N]; struct Nod { int x, y, id; } b[N]; ``` 这样,在最后求 $A$ 到 $B$ 城市的最少消耗时,就能够这样实现了(个人认为这样方便点): ``` void print() { double res = 1000000000.0; for(int i = 1; i <= idx; i++) //idx表示机场个数 for(int j = 1; j <= idx; j++) if(b[i].id == A && b[j].id == B) if(res > f[i][j]) res = f[i][j]; printf("%.1lf\n", res); } ``` 最后,别忘了初始化做 Floyd 用的 $f$ 数组。 ### 完整代码实现: ``` #include <bits/stdc++.h> #define int long long using namespace std; const int N = 1e2 + 5; struct Node { double x[5], y[5], t; } a[N]; struct Nod { int x, y, id; } b[N]; int T, n, A, B, idx; double t, newx, newy, f[N][N]; double dis2(double a1, double b1, double a2, double b2) { return (a1 - a2) * (a1 - a2) * 1.0 + (b1 - b2) * (b1 - b2) * 1.0; } double dis(double a1, double b1, double a2, double b2) { return (double)sqrt((a1 - a2) * (a1 - a2) * 1.0 + (b1 - b2) * (b1 - b2) * 1.0); } void work(int xx) { int ap, bp, cp; for(int i = 0; i <= 2; i++) { for(int j = 0; j <= 2; j++) { for(int l = 0; l <= 2; l++) { if(i == j || j == l || i == l) continue; double ka = dis2(a[xx].x[i], a[xx].y[i], a[xx].x[j], a[xx].y[j]); double kb = dis2(a[xx].x[j], a[xx].y[j], a[xx].x[l], a[xx].y[l]); double kc = dis2(a[xx].x[i], a[xx].y[i], a[xx].x[l], a[xx].y[l]); if(ka + kb == kc) ap = l, bp = i, cp = j; else continue; } } } double midx = (a[xx].x[ap] + a[xx].x[bp]) / 2.0, midy = (a[xx].y[ap] + a[xx].y[bp]) / 2.0; midx *= 2, midy *= 2; newx = midx - a[xx].x[cp], newy = midy - a[xx].y[cp]; return; } void init() { for(int i = 1; i <= idx; i++) { for(int j = 1; j <= idx; j++) { if(b[i].id == b[j].id) f[i][j] = f[j][i] = dis(b[i].x, b[i].y, b[j].x, b[j].y) * a[b[i].id].t; else f[i][j] = f[j][i] = dis(b[i].x, b[i].y, b[j].x, b[j].y) * t; } } return; } void Floyd() { for(int k = 1; k <= idx; k++) for(int i = 1; i <= idx; i++) for(int j = 1; j <= idx; j++) if(f[i][j] > f[i][k] + f[k][j]) f[i][j] = f[i][k] + f[k][j]; return; } void print() { double res = 1000000000.0; for(int i = 1; i <= idx; i++) for(int j = 1; j <= idx; j++) if(b[i].id == A && b[j].id == B) if(res > f[i][j]) res = f[i][j]; printf("%.1lf\n", res); } signed main() { ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr); cin >> T; while(T--) { cin >> n >> t >> A >> B; idx = 0; for(int i = 1; i <= n; i++) { cin >> a[i].x[0] >> a[i].y[0] >> a[i].x[1] >> a[i].y[1] >> a[i].x[2] >> a[i].y[2] >> a[i].t; newx = newy = 0; work(i); a[i].x[3] = newx, a[i].y[3] = newy; for(int j = 0; j <= 3; j++) b[++idx].id = i, b[idx].x = a[i].x[j], b[idx].y = a[i].y[j]; } init(), Floyd(), print(); } return 0; } ```