一、理清概念
牧区: 对应一个点
牧区之间的距离:实际上是两点之间的 最短路。 不要理解成欧几里得距离。只有 直接连接 的时候,才可以计算欧几里得距离。
牧场: 一个连通块
牧场直径: 一个牧场的直径是这个牧场所有的牧区(点)之间 距离 的最大值。 说的绕一点就是 最短路的最大值。
使用一条边连接两个牧场,使得合成的一个新的牧场的直径最小。意思是加入一条边之后,使得新的牧场的所有点对之间 最短路 的最大值 最小。
由于最后连接两个牧场的路线可以在两个连通块间的点枚举添加,目标是直径最小,那么取出添加的最小值。
但这个最小值未必就是答案,原因是就算你这个最小,但问题是原来连通块中的直径可能还比这个值大,那么根据定义,原来的直径才算是现在连通后真正的直径,还需要\(PK\)一下最大值。太绕了,终于说清楚了...
二、代码实现
#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
const int N = 152;
const int INF = 0x3f3f3f3f;
PII q[N]; //每个牧区的坐标
char g[N][N]; //地图
double d[N][N]; //每两个牧区之间的距离
double maxd[N]; //距离牧区i最远的最短距离是多少
//计算两个牧区之间的距离
double get_dist(PII a, PII b) {
int x = a.x - b.x, y = a.y - b.y;
return sqrt(x * x + y * y);
}
int main() {
//牧区小,牧场大
int n; //牧区数
cin >> n;
for (int i = 0; i < n; i++) cin >> q[i].x >> q[i].y; //牧区的坐标
//一个对称邻接矩阵,图的样子,g是一个二维char数组,一次读入一行
for (int i = 0; i < n; i++) cin >> g[i];
//遍历行与列,计算出每两个牧区之间的距离
//距离只在同一连通块中存在,不同的连通块间的距离是INF
//自己与自己的距离是0
//如果两个牧区相连,那么距离的计算办法是sqrt((x1-x2)^2+(y1-y2)^2)
//欧几里得距离
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i == j)
d[i][j] = 0;
else if (g[i][j] == '1')
d[i][j] = get_dist(q[i], q[j]);
else //注意:由于d数组是一个double类型,不能用memset(0x3f)进行初始化正无穷
d[i][j] = INF;
}
}
// Floyd算法模板
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
double res1 = 0; //未建设两个连通块之间线路前,已知的牧区间最大距离是多少
double res2 = INF;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) //求i到离i(最短距离)最远点的距离
/*注意一下这个INF/2的用法,很经典,松弛操作后不见的是INF
只有小于INF/2才是真正被更新的了有效距离*/
if (d[i][j] < INF / 2) maxd[i] = max(maxd[i], d[i][j]);
// res1保持原来(两个牧场中)任意两个牧区间的最大距离(直径)
res1 = max(res1, maxd[i]);
}
//连线操作,更新新牧场直径
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (d[i][j] > INF / 2) //如果i,j不在同一个连通块内
//连接原来不在同一连通块中的两个点后,可以取得的最小直径
res2 = min(res2, maxd[i] + maxd[j] + get_dist(q[i], q[j]));
// PK一下
printf("%.6lf\n", max(res1, res2));
return 0;
}