题目描述
农民John的农场里有很多牧区,有的路径连接一些特定的牧区。
一片所有连通的牧区称为一个牧场。
但是就目前而言,你能看到至少有两个牧区不连通。
现在,John想在农场里添加一条路径(注意,恰好一条)。
一个牧场的直径就是牧场中最远的两个牧区的距离(本题中所提到的所有距离指的都是最短的距离)。
考虑如下的两个牧场,每一个牧区都有自己的坐标:
1.png
图 1 是有 5 个牧区的牧场,牧区用“*”表示,路径用直线表示。
图 1 所示的牧场的直径大约是 12.07106, 最远的两个牧区是 A 和 E,它们之间的最短路径是 A-B-E。
图 2 是另一个牧场。
这两个牧场都在John的农场上。
John将会在两个牧场中各选一个牧区,然后用一条路径连起来,使得连通后这个新的更大的牧场有最小的直径。
注意,如果两条路径中途相交,我们不认为它们是连通的。
只有两条路径在同一个牧区相交,我们才认为它们是连通的。
现在请你编程找出一条连接两个不同牧场的路径,使得连上这条路径后,这个更大的新牧场有最小的直径。
输入格式
第 1 行:一个整数 N, 表示牧区数;
第 2 到 N+1 行:每行两个整数 X,Y, 表示 N 个牧区的坐标。每个牧区的坐标都是不一样的。
第 N+2 行到第 2*N+1 行:每行包括 N 个数字 ( 0或1 ) 表示一个对称邻接矩阵。
例如,题目描述中的两个牧场的矩阵描述如下:
A B C D E F G H
A 0 1 0 0 0 0 0 0
B 1 0 1 1 1 0 0 0
C 0 1 0 0 1 0 0 0
D 0 1 0 0 1 0 0 0
E 0 1 1 1 0 0 0 0
F 0 0 0 0 0 0 1 0
G 0 0 0 0 0 1 0 1
H 0 0 0 0 0 0 1 0
输入数据中至少包括两个不连通的牧区。
输出格式
只有一行,包括一个实数,表示所求答案。
数字保留六位小数。
数据范围
1≤N≤150,
0≤X,Y≤105
样例
输入样例:
8
10 10
15 10
20 10
15 15
20 15
30 15
25 10
30 10
01000000
10111000
01001000
01001000
01110000
00000010
00000101
00000010
输出样例:
22.071068
Floyd
题意:有若干个连通块,每个连通块都有直径,直径是最远的两个点的距离(最短距离),要在连通块间建立路径,使得最终的连通块的直径最短;
在连通块之间连接路径使得最终得到的整个连通块的直径最小,就是使整个连通块中距离最大的两个点的距离最小。对某两个连通块A,B而言,从A,B中分别选取i点和j点进行连接,那么这样链接得到的连通块的直径就是A中距离i点的最远距离加上B中距离j的最远距离加上i到j的距离,并与A,B的直径取最大值。
这个题目只需要求连接两个连通块后得到的最大直径
既然要求任意两点间的距离,那么用Floyd是很合适的。Floyd是基于DP的思想
三重循环,k,i,j:表示从i到j,只经过1~k个点,状态转移如下图(还是贴上课的图,清晰明了)
在计算的时候其实并没有用三维数组,而是用二维,也就是把第一维k去掉了,那我们先直接去掉,看看是否可以:
不去:d(k,i,j)=min{d(k-1,i,j),d(k-1,i,k)+d(k-1,k,j)}
去掉:d( i,j)=min{d( i,j),d( i,k)+d( k,j)}
那么去掉之后,涉及到k的d(i,k)+d(k,j),考虑当j=k的时候,d(i,k)=min{d(i,k),d(i,k)+d(k,k)}
而d(k,k)在开始是初始化为0,那么就是说在k这重循环中d(i,k)用的依旧是第k-1的量,所以可以直接去掉,d(k,j)同理。
C++ 代码
#include<iostream>
#include<cstring>
#include<cmath>
#define x first
#define y second
using namespace std;
const int N=160;
const double INF=1e20;
typedef pair<double,double> PDD ;
PDD q[N];
char g[N][N];
double d[N][N],maxd[N];
int n;
double get_dist(int i,int j){
double t=(q[i].x-q[j].x)*(q[i].x-q[j].x)+(q[i].y-q[j].y)*(q[i].y-q[j].y);
return sqrt(t);
}
int main(){
cin>>n;
for(int i=0;i<n;i++) cin>>q[i].x>>q[i].y;
for(int i=0;i<n;i++) cin>>g[i];
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==j) d[i][j]=0;//自己到自己要初始化为0,否则后面会更新自己到自己的距离
else if(g[i][j]=='1') d[i][j]=get_dist(i,j);
else d[i][j]=INF;
}
}
for(int k=0;k<n;k++){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(d[i][j]>d[i][k]+d[k][j]) d[i][j]=d[i][k]+d[k][j];
}
}
}
double r1=0;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++)
if(d[i][j]<INF/2) maxd[i]=max(maxd[i],d[i][j]);
r1=max(r1,maxd[i]);
}
double r2=INF;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(d[i][j]>INF/2) r2=min(r2,maxd[i]+maxd[j]+get_dist(i,j));
}
}
printf("%lf",max(r1,r2));
return 0;
}