[NOIP2001 提高组] Car 的旅行路线(C++,计算几何)

题目描述

又到暑假了,住在城市 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 1n10 1 ≤ s ≤ 100 1\le s \le 100 1s100 1 ≤ A , B ≤ s 1\le A,B \le s 1A,Bs

【题目来源】

NOIP 2001 提高组第四题

解题思路:

首先考虑应该用什么算法解题

先试着构思一下暴力搜索,那样需要遍历每一种可能的路径,绝对不能接受

然后继续试着采用方法优化搜索

动态规划?想一想采用动态规划的两个条件

(1)无后效性:后续的选择不会影响之前的选择

(2)最优子结构:可以从小规模的子问题推导出更大规模的问题,也就是分治的思想

看一看(1),应该是满足了,但是(2)呢?根本找不到子结构的存在

那么换一种思路优化搜索,尝试一下二分

二分的使用条件是问题的单调性

随着花费的增加,我们能够到达的城市越来越多,问题具有单调性

但是我们该如何尝试路径的搜索呢?

那就需要建一张图…等等,图?

那不就变成了单源最短路径也就是Dijkstra问题了吗

但是好像不太对劲,一个城市中有四个机场…

那么就采用Floyd

考虑一下数据量,只需要一个含有 ( 100 ∗ 4 ) 2 (100 * 4)^2 (1004)2元素的二维数组就可以了,可以接受

那么计算时间复杂度 o ( ( 100 ∗ 4 ) 3 ) o((100 * 4) ^ 3) o((1004)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+x2x3y4=y1+y2y3
然后是边权的计算问题:城市内部是高铁价格,外部是飞机价格

判断机场是否在同一个城市,采用方法是机场索引 / 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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WitheredSakura_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值