洛谷P1522 [USACO2.4]牛的旅行 Cow Tours

洛谷P1522 [USACO2.4]牛的旅行 Cow Tours

题意:

给出一些牧区的坐标,以及一个用邻接矩阵表示的牧区之间图。如果两个牧区之间有路存在那么这条路的长度就是两个牧区之间的欧几里得距离。

对于一个联通块,称之为一个牧场,也就是说一个牧场内任意一个牧区都可以到达该牧场内的任意的另外一个牧区。

对于一个牧场,它的直径是这个联通块内最短路的最大值。

现在让你在恰当地选择两个牧场,在这两个牧场中各自选一个牧区,在这两个牧区之间建路,要求建路之后所有牧场中最大的直径最小。这里其实如果产生了新的最大直径(也可能不产生新的最大直径),那么一定是在这个新的农场中产生的,所以这里不如说恰当选择两个牧场从中选择两个牧区建边,使得产生的新的牧场的直径最小。

输出建边之后所有农场中最大的直径。


题解:

很好的一道最短路题。

先按照题目要求建立邻接矩阵,之后再这个邻接矩阵中跑一次\(floyd\)

用一个数组\(MaxDis\)维护对于每个牧区,它所在的牧场中离它最远的牧区的距离是多少。

这样枚举每个不存在边的牧场对,当假设他们连接之后,构成的新牧场的可能的直径\(d_{uv}=MaxDis[u]+MaxDis[v]+getDis(u,v)\),这里之所以说可能是因为可能原来\(u\)\(v\)所在的农场直径比这个\(d_{uv}\)要大。

这里不需要考虑太细节,只需要用一个变量记录在不添加边的时候,所有牧场中直径的最大值,然后找到添加边之后最小的\(d\),在这两个值之间取较大的既是答案。原因很简单,我们担心的是\(d\)可能比原来的\(u\)\(v\)所在牧场的直径小,所以新牧场的直径实际上是\(max\{d_{cur},d_u,d_v\}\),之后答案取\(min\{max\{d_{u_1v_1},d_{u_1},d_{v_1}\},d_{u_2v_2},d_{u_2},d_{v_2}\},...,d_{u_xv_x},d_{u_x},d_{v_x}\}\}\),这个等价于\(max\{d_{f_1},d_{f_2},...,d_{f_n},min\{d_{u_1v_1},d_{u_2v_2},...,d_{u_xv_x}\}\}=max\{max\{d_{f_i}\},min\{d_{u_jv_j}\}\}\),其中\(d_{f_i}\)表示第\(i\)个牧场的直径。


AC代码:

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

const int Maxn = 200;
const double INF = 1e7;
double dis[Maxn][Maxn], pos[Maxn][2], MaxDis[Maxn];

double getDis(int x, int y) {
	return sqrt(pow(pos[x][0] - pos[y][0], 2) + pow(pos[x][1] - pos[y][1], 2));
}

void solve() {
	int n, t;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%lf %lf", &pos[i][0], &pos[i][1]);
	}
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			scanf("%1d", &t);
			if (t == 1) {
				dis[i][j] = dis[j][i] = getDis(i, j);
			} else if (i != j) {
				dis[i][j] = dis[j][i] = INF;
			}
		}
	}
	for (int k = 0; k < n; k++) {
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < n; j++) {
				dis[i][j] = std::min(dis[i][j], dis[i][k] + dis[k][j]);
			}
		}
	}
	double max1 = 0, max2 = INF;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			if (dis[i][j] != INF) {
				MaxDis[i] = std::max(MaxDis[i], dis[i][j]);
				max1 = std::max(max1, dis[i][j]);
			}
		}
	}
	for (int i = 0; i < n; i++) {
		for (int j = i + 1; j < n; j++) {
			if (dis[i][j] == INF) {
				max2 = std::min(max2, MaxDis[i] + MaxDis[j] + getDis(i, j));
			}
		}
	}
	printf("%.6f\n", std::max(max1, max2));
}

int main() {
	solve();
	return 0;
}

posted @ 2021-02-07 16:40  牟翔宇  阅读(182)  评论(0编辑  收藏  举报