题目描述
又到暑假了,住在城市 A 的 Car 想和朋友一起去城市旅游。
她知道每个城市都有
4
4
4 个飞机场,分别位于一个矩形的
4
4
4 个顶点上,同一个城市中两个机场之间有一条笔直的高速铁路,第
i
i
i 个城市中高速铁路的单位里程价格为
T
i
T_i
Ti,任意两个不同城市的机场之间均有航线,所有航线单位里程的价格均为
t
t
t。
图例(从上而下)
机场
高速铁路
飞机航线
注意:图中并没有标出所有的铁路与航线。
那么 Car 应如何安排到城市B的路线才能尽可能的节省花费呢?她发现这并不是一个简单的问题,于是她来向你请教。
找出一条从城市 A 到 B 的旅游路线,出发和到达城市中的机场可以任意选取,要求总的花费最少。
输入格式
第一行为一个正整数 n n n,表示有 n n n 组测试数据。
每组的第一行有 4 4 4 个正整数 s , t , A , B s,t,A,B s,t,A,B。
s s s 表示城市的个数, t t t 表示飞机单位里程的价格, A A A, B B B 分别为城市A,B 的序号。
接下来有 s s s 行,其中第 i i i 行均有 7 7 7 个正整数 x i 1 , y i 1 , x i 2 , y i 2 , x i 3 , y i 3 , T i x_{i1},y_{i1},x_{i2},y_{i2},x_{i3},y_{i3},T_i xi1,yi1,xi2,yi2,xi3,yi3,Ti ,这当中的 ( x i 1 , y i 1 x_{i1},y_{i1} xi1,yi1),( x i 2 , y i 2 x_{i2},y_{i2} xi2,yi2),( x i 3 , y i 3 x_{i3},y_{i3} xi3,yi3)分别是第 i i i 个城市中任意 3 3 3 个机场的坐标, T i T_i Ti 为第 i i i 个城市高速铁路单位里程的价格。
输出格式
共有
n
n
n 行,每行
1
1
1 个数据对应测试数据。
保留一位小数。
样例 #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
%
100\%
100% 的数据,
1
≤
n
≤
10
1\le n \le 10
1≤n≤10,
1
≤
s
≤
100
1\le s \le 100
1≤s≤100,
1
≤
A
,
B
≤
s
1\le A,B \le s
1≤A,B≤s
【题目来源】
NOIP 2001 提高组第四题
解题思路:
首先考虑应该用什么算法解题
先试着构思一下暴力搜索,那样需要遍历每一种可能的路径,绝对不能接受
然后继续试着采用方法优化搜索
动态规划?想一想采用动态规划的两个条件
(1)无后效性:后续的选择不会影响之前的选择
(2)最优子结构:可以从小规模的子问题推导出更大规模的问题,也就是分治的思想
看一看(1),应该是满足了,但是(2)呢?根本找不到子结构的存在
那么换一种思路优化搜索,尝试一下二分
二分的使用条件是问题的单调性
随着花费的增加,我们能够到达的城市越来越多,问题具有单调性
但是我们该如何尝试路径的搜索呢?
那就需要建一张图…等等,图?
那不就变成了单源最短路径也就是Dijkstra
问题了吗
但是好像不太对劲,一个城市中有四个机场…
那么就采用Floyd
考虑一下数据量,只需要一个含有 ( 100 ∗ 4 ) 2 (100 * 4)^2 (100∗4)2元素的二维数组就可以了,可以接受
那么计算时间复杂度 o ( ( 100 ∗ 4 ) 3 ) o((100 * 4) ^ 3) o((100∗4)3),然后最多询问次数为 10 10 10,计算量也可以接受
算法确定,接下来考虑如何用代码实现
首先第一个要处理的问题就是矩形只给出了三个点,我们需要自行计算第四个点
看起来很简单,但是题中给的是任意三个点…
根据矩形对角线互相平分,求第四个点的位置
{
x
4
=
x
1
+
x
2
−
x
3
y
4
=
y
1
+
y
2
−
y
3
\begin{cases} x_4 = x_1 + x_2 - x_3\\[1ex] y_4 = y_1 + y_2 - y_3 \end{cases}
{x4=x1+x2−x3y4=y1+y2−y3
然后是边权的计算问题:城市内部是高铁价格,外部是飞机价格
判断机场是否在同一个城市,采用方法是机场索引 / 4
向下取整
两个问题解决之后就可以愉快地Floyd
了
这里提醒大家一点关于Floyd
的三重循环顺序
必须是“尝试把每一个点加入每一条路径中”,而不是”尝试在每一条路径中加入每一个点“
前者的代码实现是这样的
for (int k = 0; k < s * 4; k++) {
for (int i = 0; i < s * 4; i++) {
for (int j = 0; j < s * 4; j++) {
if (i == j) continue;
min_dist[i][j] = min(min_dist[i][j], min_dist[i][k] + min_dist[k][j]);
}
}
}
后者的代码实现是这样的
for (int i = 0; i < s * 4; i++) {
for (int j = 0; j < s * 4; j++) {
for (int k = 0; k < s * 4; k++) {
if (i == j) continue;
min_dist[i][j] = min(min_dist[i][j], min_dist[i][k] + min_dist[k][j]);
}
}
}
区别就在于k
是第一重还是第三重,很容易忽略的一个问题
至于原因,各位自己尝试一下就知道了:后者可能会造成最短路更新不完全的问题
最后,AC代码如下
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
const int max_s = 100;
const int NaN = 0x3F3F3F3F;
int n, s, A, B;
long long t;
//存图
struct city { long long xs[4], ys[4], price; }citys[max_s + 1];
double min_dist[max_s * 4][max_s * 4];
//边权计算
inline double distance(int c1, int c2) {
city city1 = citys[c1 / 4 + 1];
city city2 = citys[c2 / 4 + 1];
if (c1 / 4 == c2 / 4) {
return sqrt((city1.xs[c1 % 4] - city1.xs[c2 % 4]) * (city1.xs[c1 % 4] - city1.xs[c2 % 4]) +
(city1.ys[c1 % 4] - city1.ys[c2 % 4]) * (city1.ys[c1 % 4] - city1.ys[c2 % 4])) * city1.price;
}
else {
return sqrt((city1.xs[c1 % 4] - city2.xs[c2 % 4]) * (city1.xs[c1 % 4] - city2.xs[c2 % 4]) +
(city1.ys[c1 % 4] - city2.ys[c2 % 4]) * (city1.ys[c1 % 4] - city2.ys[c2 % 4])) * t;
}
}
void floyd() {
//初始化
for (int i = 0; i < s * 4; i++) {
for (int j = 0; j < s * 4; j++) {
if (i == j) continue;
min_dist[i][j] = distance(i, j);
}
}
//多源最短路
for (int k = 0; k < s * 4; k++) {
for (int i = 0; i < s * 4; i++) {
for (int j = 0; j < s * 4; j++) {
if (i == j) continue;
min_dist[i][j] = min(min_dist[i][j], min_dist[i][k] + min_dist[k][j]);
}
}
}
}
//计算第四节点
inline void add_node(int c) {
long long x1 = citys[c].xs[0], y1 = citys[c].ys[0];
long long x2 = citys[c].xs[1], y2 = citys[c].ys[1];
long long x3 = citys[c].xs[2], y3 = citys[c].ys[2];
long long t1 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
long long t2 = (x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3);
long long t3 = (x3 - x1)* (x3 - x1) + (y3 - y1) * (y3 - y1);
if (t1 + t2 == t3) {
citys[c].xs[3] = x1 + x3 - x2;
citys[c].ys[3] = y1 + y3 - y2;
}
else if (t2 + t3 == t1) {
citys[c].xs[3] = x1 + x2 - x3;
citys[c].ys[3] = y1 + y2 - y3;
}
else {
citys[c].xs[3] = x2 + x3 - x1;
citys[c].ys[3] = y2 + y3 - y1;
}
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) {//n组数据
cin >> s >> t >> A >> B;
for (int j = 1; j <= s; j++) {//存图
for (int k = 0; k < 3; k++)
cin >> citys[j].xs[k] >> citys[j].ys[k];
add_node(j);
cin >> citys[j].price;
}
floyd();//floyd
double ans = NaN;//输出
if (A == B) {
ans = 0.0;
}
else {
int start1 = (A - 1) * 4, end1 = start1 + 4;
int start2 = (B - 1) * 4, end2 = start2 + 4;
for (int j = start1; j < end1; j++) {
for (int k = start2; k < end2; k++) {
ans = min(ans, min_dist[j][k]);
}
}
}
cout << setiosflags(ios::fixed) << setprecision(1) << ans << endl;
}
return 0;
}