洛谷 P1027 [NOIP2001 提高组] Car 的旅行路线【推公式+勾股定理+最短路】

文章介绍了使用Floyd算法解决城市间旅行路线问题,涉及机场坐标、价格计算和最短路径求解,旨在找到从A到B的最低花费路线。
摘要由CSDN通过智能技术生成

原题链接:https://www.luogu.com.cn/problem/P1027

题目描述

又到暑假了,住在城市 A 的 Car 想和朋友一起去城市旅游。
她知道每个城市都有 4 个飞机场,分别位于一个矩形的 4 个顶点上,同一个城市中两个机场之间有一条笔直的高速铁路,第 i 个城市中高速铁路了的单位里程价格为 Ti​,任意两个不同城市的机场之间均有航线,所有航线单位里程的价格均为 t。

注意:图中并没有标出所有的铁路与航线。

那么 Car 应如何安排到城市 B 的路线才能尽可能的节省花费呢?她发现这并不是一个简单的问题,于是她来向你请教。

找出一条从城市 A 到 B 的旅游路线,出发和到达城市中的机场可以任意选取,要求总的花费最少。

输入格式

第一行为一个正整数 n,表示有 n 组测试数据。

每组的第一行有 4 个正整数 S,t,A,B。

S 表示城市的个数,t 表示飞机单位里程的价格,A,B 分别为城市A,B 的序号。

接下来有 S 行,其中第 i 行均有 7 个正整数xi1​,yi1​,xi2​,yi2​,xi3​,yi3​,Ti​,这当中的 (xi1​,yi1​),(xi2​,yi2​),(xi3​,yi3​),分别是第 i 个城市中任意 3 个机场的坐标,Ti​ 为第 i 个城市高速铁路单位里程的价格。

输出格式

共有 n 行,每行 1 个数据对应测试数据。
保留一位小数。

输入输出样例

输入 #1

1
3 10 1 3
1 1 1 3 3 1 30
2 5 7 4 5 2 1
8 6 8 8 11 6 3

输出 #1

47.5

说明/提示

【数据范围】
对于 100% 的数据,1≤n≤10,1≤S≤100,1≤A,B≤S。

【题目来源】

NOIP 2001 提高组第四题

解题思路:

首先我们对于每一个城市题目只给了我们三个机场的坐标,那么我们肯定需要求出第四个机场的坐标,下面画个图描述一下:

此时每个城市的四个机场的坐标都已经知道了,那么我们不妨进行编号,对于第一个城市的四个机场编号为1,2,3,4,对第二个城市的四个机场编号为5,6,7,8,后面的城市依此类推,假设某个机场的编号为k,那么我们可以通过(k-1)/4+1求出这个机场属于第几个城市,我们用d[i][j]记录机场i到机场j的最小花费,然后就将问题转换为了求A城市到B城市的最短路,由于S最大是100,也就是最多只有100个城市,那么最多只有400个机场,也就是图中最多只有400个点,那么我们可以直接采用floyd求最短路即可,求完最短路之后,最终答案就是从A中四个机场中的任意一个机场到达B中任意一个机场的最短路,直接遍历求一遍答案即可。

时间复杂度:O(n^3),floyd算法的时间复杂度为O(n^3)。

空间复杂度:O(400^2),因为最多有400个点。

cpp代码如下:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;
typedef long long LL;

const int N = 110, M = 410;

int n;
int S, A, B, t;
int x[M], y[M], T[N];
double d[M][M];

double dist(int x1, int y1, int x2, int y2)  //求俩点的距离
{
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
double dist_square(int x1, int y1, int x2, int y2)  //求俩个点距离的平分
{
    return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
}

void floyd()  //floyd算法求最短路
{
    for (int k = 1; k <= S * 4; k++)
        for (int i = 1; i <= S * 4; i++)
            for (int j = 1; j <= S * 4; j++)
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
int main()
{
    cin >> n;
    while (n--)
    {
        scanf("%d%d%d%d", &S, &t, &A, &B);
        for (int i = 1; i <= S; i++)
        {
            scanf("%d%d%d%d%d%d%d", &x[(i - 1) * 4 + 1], &y[(i - 1) * 4 + 1], &x[(i - 1) * 4 + 2], &y[(i - 1) * 4 + 2], &x[(i - 1) * 4 + 3], &y[(i - 1) * 4 + 3], &T[i]);
            int dab = dist_square(x[(i - 1) * 4 + 1], y[(i - 1) * 4 + 1], x[(i - 1) * 4 + 2], y[(i - 1) * 4 + 2]);
            int dac = dist_square(x[(i - 1) * 4 + 1], y[(i - 1) * 4 + 1], x[(i - 1) * 4 + 3], y[(i - 1) * 4 + 3]);
            int dbc = dist_square(x[(i - 1) * 4 + 2], y[(i - 1) * 4 + 2], x[(i - 1) * 4 + 3], y[(i - 1) * 4 + 3]);
            if (dab + dac == dbc)  //推公式,然后根据勾股定理求出对角线,然后根据推出的公式求出第四个点的坐标
            {
                x[i * 4] = x[(i - 1) * 4 + 2] + x[(i - 1) * 4 + 3] - x[(i - 1) * 4 + 1];
                y[i * 4] = y[(i - 1) * 4 + 2] + y[(i - 1) * 4 + 3] - y[(i - 1) * 4 + 1];
            }
            if (dab + dbc == dac)
            {
                x[i * 4] = x[(i - 1) * 4 + 1] + x[(i - 1) * 4 + 3] - x[(i - 1) * 4 + 2];
                y[i * 4] = y[(i - 1) * 4 + 1] + y[(i - 1) * 4 + 3] - y[(i - 1) * 4 + 2];
            }
            if (dac + dbc == dab)
            {
                x[i * 4] = x[(i - 1) * 4 + 1] + x[(i - 1) * 4 + 2] - x[(i - 1) * 4 + 3];
                y[i * 4] = y[(i - 1) * 4 + 1] + y[(i - 1) * 4 + 2] - y[(i - 1) * 4 + 3];
            }
        }

        memset(d, 0, sizeof d);  //预处理任意俩点之间的距离
        for (int i = 1; i <= S * 4; i++)
            for (int j = 1; j <= S * 4; j++)
            {
                if (i == j)
                    continue;
                if ((i - 1) / 4 == (j - 1) / 4)
                    d[i][j] = dist(x[i], y[i], x[j], y[j]) * T[(i - 1) / 4 + 1];
                else
                    d[i][j] = dist(x[i], y[i], x[j], y[j]) * t;
            }

        floyd();  //floyd求最短路

        double ans = 2e9;  //要求从A城市到B城市的最短路就是从城市A中四个机场中的任意一个机场的到B城市中四个机场中的任意一个机场的最短路
        for (int i = 1; i <= 4; i++)
            for (int j = 1; j <= 4; j++)
                ans = min(ans, d[(A - 1) * 4 + i][(B - 1) * 4 + j]);

        printf("%.1lf\n", ans);  //输出答案,保留一位小数
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值