P1522 [USACO2.4]牛的旅行 Cow Tours
题意
直径:一个牧场(连通块)中任意两点最短路的最大值。
给你一张无向带权图,图中有一些连通块,试连接图中两个不同的连通块中的一点,是新的连通块直径最小。
分析
-
我们需要把图存下来,于是用 代表边权。
-
我们需要找出图中的连通块,于是 对每个块染色,把每个连通块的点放进对应的 里。
-
我们需要知道任意两点之间的最短路,于是跑了
floyd次 求最短路 。 -
我们需要知道每个连通块的直径 ,于是我们需要知道每个点到块中其他点的最短路的最大值 ,就可以知道 。
-
我们需要找出答案,于是暴力枚举两个点 ,如果 不在同一个连通块内,那么连接后的新直径可能在两个原来的连通块内,也可能是连接之后形成的,也就是 。答案就是所有情况的最小值。
代码
#include<bits/stdc++.h>
#define qw first
#define er second
using namespace std;
const int N=200;
int n,c[N];//c[i]是i所在的连通块
double x[N],y[N],w[N][N],d[N][N],mxd[N],cz[N],mx=1e9;
bool a[N][N],v[N];
vector<int>c2[N];
priority_queue<pair<double,int> >q;
pair<double,int>e;
void dfs(int x,int col){
if(v[x]) return;
v[x]=1;c[x]=col;
c2[col].push_back(x);
for(int i=1;i<=n;i++)
if(a[x][i])
dfs(i,col);
}
void Dijkstra(int s){
memset(v,0,sizeof(v));
d[s][s]=e.qw=0;e.er=s;
q.push(e);
while(q.size()){
int x=q.top().er;
q.pop();
if(v[x])
continue;
v[x]=1;
for(int i=1;i<=n;i++){
if(!a[x][i])
continue;
if(d[s][i]>d[s][x]+w[x][i]){
d[s][i]=d[s][x]+w[x][i];
e.qw=-d[s][i];e.er=i;
q.push(e);
}
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=1e7;
for(int i=1;i<=n;i++)
cin>>x[i]>>y[i];
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%1d",&a[i][j]);
w[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
}
for(int i=1;i<=n;i++)
Dijkstra(i);
memset(v,0,sizeof(v));
for(int i=1,col=1;i<=n;i++){
if(v[i]) continue;
dfs(i,col);
for(int j=0;j<c2[col].size();j++){
for(int k=0;k<c2[col].size();k++){
if(j==k||d[c2[col][j]][c2[col][k]]==1e7) continue;
mxd[c2[col][j]]=max(mxd[c2[col][j]],d[c2[col][j]][c2[col][k]]);
}
cz[col]=max(cz[col],mxd[c2[col][j]]);
}
col++;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(c[i]==c[j]) continue;
mx=min(mx,max(cz[c[i]],max(cz[c[j]],mxd[i]+w[i][j]+mxd[j])));
}
printf("%.6lf",mx);
return 0;
}

浙公网安备 33010602011771号