P1027 [NOIP 2001 提高组] Car 的旅行路线 题解
P1027 [NOIP 2001 提高组] Car 的旅行路线
简化题意
有 \(S\) 个城市,每个城市的形状概括为一个矩形,其四个顶点上分别是四个机场。
同一个城市的四个机场之间都有公路连接,同一条公路上单位里程为 \(T_i\)。
每两个机场之间都有航线,所有航线单位里程的价格都是 \(t\)。
现在,我们希望求出从城市 \(A\) 到城市 \(B\) 的最少价值(到达一个城市的任意一个机场都算到了这个城市)。
特别的,对于每个城市,只会给出矩形的三个顶点的坐标。
解题思路
首先,这题卡住我最大的一点是,怎么用给出的三个顶点求出第四个顶点的坐标?
接下来的部分主要是数学证明,大家认真看看吧。
首先,对于给出的三个顶点,如果将它们连接起来,一定会构成一个直角三角形。
我们设:直角顶点为 \(A\)。
接下来的解法我一开始也没想到,后来去学校里请教了同学才搞懂了。
我们引入概念向量。在平面直角坐标系中,向量就像一个有长度且带方向的箭头。相当于一个构造合集,可以对于当前顶点进行构造(这个比喻可能不太好,大家尽量理解理解)。
然后,我们计算 \(C + \overrightarrow{AB}\),就得出了 \(D\) 点的坐标(这里 \(C\) 指的是 \(C\) 点的坐标)。
这个原理我来解释一下。因为矩形对边平行且相等,所以 \(A\) 顶点分别与 \(B\),\(C\) 顶点在一条直线上。因此,如果我们知道了从 \(A\) 到 \(B\) 怎么走(即 \(\overrightarrow{AB}\)),再从 \(C\) 点走同样的距离,就可以到达点 \(D\) 了。
然后问题来了,我们怎么知道哪个顶点是目前三个顶点组成的直角三角形的直角顶点?
这里运用了勾股定理:
好了,至此数学证明结束。我们已经得到了第四个顶点的坐标。
接下来的过程,就是进行最短路计算了。
我这里的思路是:把机场的四个顶点坐标全都记录下来,然后同一个机场内的顶点距离用公路来计算,首先预处理好。
然后,把每两个不同城市之间的机场的距离也预处理好。
然后跑一遍 Floyd 就求出每两个机场之间的最短距离了。
最后,因为两个城市的每两个机场之间互相到达都行,我们就枚举两个城市之间的机场的最短距离,最后输出最短路径距离,这题就解决了!
代码部分
#include <bits/stdc++.h>
using namespace std;
int n, t, A, B;
double dis[410][410];
vector<pair<int, int> > g;
struct node
{
int xa, ya, xb, yb, xc, yc, xd, yd, v;
}a[110];
int dist(int xa, int ya, int xb, int yb)//两点之间距离公式
{
return (xa - xb) * (xa - xb) + (ya - yb) * (ya - yb);
}
void floyd()//Floyd 求最短路
{
for(int k = 0;k < g.size();k ++)
for(int i = 0;i < g.size();i ++)
for(int j = 0;j < g.size();j ++)
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
int main()
{
int T;
cin >> T;
while(T --)
{
cin >> n >> t >> A >> B;
//初始化:清空数组
g.clear();
//初始化为极大值
for(int i = 0;i <= n * 4;i ++)
for(int j = 0;j <= n * 4;j ++)
dis[i][j] = 1e9;
for(int i = 1;i <= n;i ++)
{
cin >> a[i].xa >> a[i].ya >> a[i].xb >> a[i].yb >> a[i].xc >> a[i].yc >> a[i].v;
//求哪个顶点是直角顶点并找出 D 点的坐标
if(dist(a[i].xa, a[i].ya, a[i].xb, a[i].yb) + dist(a[i].xa, a[i].ya, a[i].xc, a[i].yc) == dist(a[i].xb, a[i].yb, a[i].xc, a[i].yc))
a[i].xd = a[i].xb + a[i].xc - a[i].xa, a[i].yd = a[i].yb + a[i].yc - a[i].ya;
else if(dist(a[i].xa, a[i].ya, a[i].xb, a[i].yb) + dist(a[i].xb, a[i].yb, a[i].xc, a[i].yc) == dist(a[i].xa, a[i].ya, a[i].xc, a[i].yc))
a[i].xd = a[i].xa + a[i].xc - a[i].xb, a[i].yd = a[i].ya + a[i].yc - a[i].yb;
else if(dist(a[i].xc, a[i].yc, a[i].xa, a[i].ya) + dist(a[i].xc, a[i].yc, a[i].xb, a[i].yb) == dist(a[i].xa, a[i].ya, a[i].xb, a[i].yb))
a[i].xd = a[i].xa + a[i].xb - a[i].xc, a[i].yd = a[i].ya + a[i].yb - a[i].yc;
//把这些坐标放到一个大数组里面
g.push_back({a[i].xa, a[i].ya});
g.push_back({a[i].xb, a[i].yb});
g.push_back({a[i].xc, a[i].yc});
g.push_back({a[i].xd, a[i].yd});
//同一个城市之间机场互相之间的距离计算
for(int j = (i - 1) * 4;j < g.size();j ++)
for(int k = (i - 1) * 4;k < g.size();k ++)
{
pair<int, int> u = g[j], v = g[k];
dis[j][k] = min(dis[j][k], sqrt((double)dist(u.first, u.second, v.first, v.second)) * a[i].v);
}
}
//不同城市之间机场的距离计算
for(int i = 0;i < g.size();i ++)
for(int j = 0;j < g.size();j ++)
{
if(i / 4 != j / 4)
{
pair<int, int> u = g[i], v = g[j];
dis[i][j] = min(dis[i][j], sqrt((double)dist(u.first, u.second, v.first, v.second)) * t);
}
}
//跑 Floyd 最短路
floyd();
//找 A、B 城市的机场之间的最短路径
double ans = 1e9;
for(int i = (A - 1) * 4;i / 4 == A - 1;i ++)
for(int j = (B - 1) * 4;j / 4 == B - 1;j ++)
{
ans = min(ans, dis[i][j]);
ans = min(ans, dis[j][i]);
}
printf("%.1lf\n", ans);
}
return 0;
}
浙公网安备 33010602011771号