D63 最短路 Floyd 算法 P1522 [USACO2.4] 牛的旅行

D63 最短路 Floyd 算法 P1522 [USACO2.4] 牛的旅行_哔哩哔哩_bilibili

 

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

连通块的直径:一个连通块中任意两点最短路的最大值。

给一张无向带权图,图中有多个连通块,尝试连接两个不同连通块中的两点,使得新连通块的直径最小。输出这个直径。

思路

连一条边 $(i,j)$ 后,新连通块的直径可能有以下情况:

  1. $i$ 所在连通块的直径;
  2. $j$ 所在连通块的直径;
  3. 从 $i$ 点出发的最远距离 $+$ 从 $j$ 点出发的最远距离 $+(i,j)$ 边的长度。

由于 $n=150$,跑一遍 Floyd,预处理出 n 个点之间的最短路。
再枚举边 $(i,j)$,只要连通,计算出每个联通块的直径。
再枚举边 $(i,j)$,只要不连通,计算出连边后的最小直径 $r1$,同时记录两个块 $a,b$。
再分别计算出 $a,b$ 块的直径 $r2,r3$。
答案就是 $max(r1,max(r2,r3))$。

// 最短路 Floyd 算法 O(n^3)
#include<bits/stdc++.h>
using namespace std;

#define pdd pair<double,double>
#define x first
#define y second
const int N=155;
const double INF=1e15; //10^5*1.414*150
int n;
pdd q[N];
char g[N][N];
double d[N][N],maxd[N];

double dis(pdd a,pdd b){
  return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int main(){
  cin>>n;
  for(int i=0; i<n; i++) cin>>q[i].x>>q[i].y; //n个点的坐标
  for(int i=0; i<n; i++) cin>>g[i]; //图的连通性
  for(int i=0; i<n; i++) //初始化d数组
    for(int j=0; j<n; j++)
      if(g[i][j]=='1') d[i][j]=dis(q[i],q[j]);
      else if(i==j) d[i][j]=0;
      else d[i][j]=INF;
      
  for(int k=0; k<n; k++) //Floyd求最短路
    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]);
        
  for(int i=0; i<n; i++) //求每个连通块的直径 maxd
    for(int j=0; j<n; j++)
      if(d[i][j]<INF/2) //如果两点连通,因浮点数精度问题不写==INF
        maxd[i]=max(maxd[i],d[i][j]);
  
  double r1=INF; int a=0,b=0;
  for(int i=0; i<n; i++) //求连边后的直径r1,记录两个块a,b
    for(int j=0; j<n; j++)
      if(d[i][j]>INF/2)  //如果两点不连通
        if(r1>maxd[i]+maxd[j]+dis(q[i],q[j]))
          r1=maxd[i]+maxd[j]+dis(q[i],q[j]),a=i,b=j;
          
  double r2=0,r3=0;
  for(int i=0; i<n; i++) //求a块的直径r2
    if(d[a][i]<INF/2) r2=max(r2,maxd[i]);
  for(int i=0; i<n; i++) //求b块的直径r3
    if(d[i][b]<INF/2) r3=max(r3,maxd[i]);
    
  printf("%.6lf\n",max(r1,max(r2,r3))); //新牧场的直径
}

 

P4306 [JSOI2010] 连通数 - 洛谷

 

posted @ 2026-02-09 19:08  董晓  阅读(146)  评论(0)    收藏  举报