Description
农民John的农场里有很多牧区。有的路径连接一些特定的牧区。一片所有连通的牧区称为一个牧场。但是就目前而言,你能看到至少有两个牧区不连通。现在,John想在农场里添加一条路径 ( 注意,恰好一条 )。对这条路径有这样的限制:一个牧场的直径就是牧场中最远的两个牧区的距离 ( 本题中所提到的所有距离指的都是最短的距离 )。考虑如下的两个牧场,图1是有5个牧区的牧场,牧区用“*”表示,路径用直线表示。每一个牧区都有自己的坐标:
图1所示的牧场的直径大约是12.07106, 最远的两个牧区是A和E,它们之间的最短路径是A-B-E。
这两个牧场都在John的农场上。John将会在两个牧场中各选一个牧区,然后用一条路径连起来,使得连通后这个新的更大的牧场有最小的直径。注意,如果两条路径中途相交,我们不认为它们是连通的。只有两条路径在同一个牧区相交,我们才认为它们是连通的。
现在请你编程找出一条连接两个不同牧场的路径,使得连上这条路径后,这个更大的新牧场有最小的直径。
图1所示的牧场的直径大约是12.07106, 最远的两个牧区是A和E,它们之间的最短路径是A-B-E。
这两个牧场都在John的农场上。John将会在两个牧场中各选一个牧区,然后用一条路径连起来,使得连通后这个新的更大的牧场有最小的直径。注意,如果两条路径中途相交,我们不认为它们是连通的。只有两条路径在同一个牧区相交,我们才认为它们是连通的。
现在请你编程找出一条连接两个不同牧场的路径,使得连上这条路径后,这个更大的新牧场有最小的直径。
Input
第 1 行:一个整数N (1 <= N <= 150), 表示牧区数;
第 2 到 N+1 行:每行两个整数X,Y ( 0 <= X,Y<= 100000 ), 表示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
输入数据中至少包括两个不连通的牧区。
第 2 到 N+1 行:每行两个整数X,Y ( 0 <= X,Y<= 100000 ), 表示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
输入数据中至少包括两个不连通的牧区。
Output
只有一行,包括一个实数,表示所求答案。数字保留六位小数。
Sample Input
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
10 10
15 10
20 10
15 15
20 15
30 15
25 10
30 10
01000000
10111000
01001000
01001000
01110000
00000010
00000101
00000010
Sample Output
22.071068
分析
很棒的一道关于图的联通块的题,以Floyd为核心加上一些技巧, 选择一个联通块的直径或者要么枚举两个联通块的顶点。转一个大佬的话:请小心:当你连接了某条路径后,所有牧场中的最大直径并不一定是你所连接的两个牧场的直径。
所以这道题其实是有特殊情况的!!!第8个数据如果你不加最后代码中的那个for循环的话会WA,只有90分。
for(re i=1;i<=n;i++)
ans=max(ans,l[i]);//特殊情况
然后我们来说一下为什么要加这个东东?因为你连接2个牧场之后的最小直径有可能就是原来的每个点到原来的任意可联通点的最大距离啊QWQ!
上代码:
//题解by disangan233 or Lixiuyu
#include<bits/stdc++.h>
using namespace std;
#define re register long long
#define ll long long
ll n;
double d,f[155][155],ans=123456789,l[155];
struct did{
ll x,y;
}a[155];
inline ll read()
{
char did=getchar();
re luoyang=0,yz=1;
while(!isdigit(did)){if(did=='-')yz=-1;did=getchar();}
while(isdigit(did))luoyang=luoyang*10+did-'0',did=getchar();
return yz*luoyang;
}
inline double jisuan(ll i,ll j)//计算距离
{
return sqrt(pow(abs(1.0*a[i].x-a[j].x),2)+pow(abs(1.0*a[i].y-a[j].y),2));
}
int main()
{
memset(f,127,sizeof(f));
n=read();
for(re i=1;i<=n;i++)
a[i].x=read(),a[i].y=read();
for(re i=1;i<=n;i++)//输入
for(re j=1;j<=n;j++)
{
char b;cin>>b;
if(b=='1')
f[i][j]=jisuan(i,j);
}
for(re k=1;k<=n;k++)//Floyd
for(re i=1;i<=n;i++)
for(re j=1;j<=n;j++)
if(i!=j&&i!=k&&j!=k)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
for(re i=1;i<=n;i++)
for(re j=1;j<=n;j++)
if(i!=j&&f[i][j]<123456789&&l[i]<f[i][j])//用l[]来储存点i到可到达任意一点的最大距离
l[i]=f[i][j];
for(re i=1;i<=n;i++)
for(re j=1;j<=n;j++)
if(i!=j&&f[i][j]>123456789)
ans=min(ans,jisuan(i,j)+l[i]+l[j]);
for(re i=1;i<=n;i++)
ans=max(ans,l[i]);//特殊情况
printf("%.6lf",ans);
return 0;
}