洛谷 P1522 牛的旅行 Cow Tours
题意
这道题意较难理解:john有多个农场,每个农场有多个农区,给出农区的位置以及农区之间是否有路线(就是邻接表),求出用最好的方法连接后(可以理解为合并后的一个农场),求出这个农场直径(相距最远的两个农区的最短路)
做法
我的做法是先将农场给划分开来,因为连接后的的直径有三种可能,先将第一个农场记作x,另一个记作y:
- x的直径;
- y的直径;
- 分别从连接点出发,x的最长路线加上y的最长路线再加上连接点的距离。
代码如下,只要理解了连通块的转换以及最短路的算法进行暴力枚举即可。
#include<bits/stdc++.h> using namespace std; int x[155],y[155]; double d[155][155], D[155][155]; int belong[155]; int p[155]; int q[155][155]; int n; double f[155]; double kuai[155][155]; void DFS(int l,int h) { belong[l]=h; q[h][++p[h]]=l;//p[h]记录这个连通块有多少个农区,q数组记录第h个连通块的第p[h]个农区 for(int i=1;i<=n;++i) { if(i==l) continue; if(!belong[i] && d[l][i]<1e9) DFS(i,h); } } double zhijing(int t,int k) { int v[155]={}; double ws[155]; for(int i=1;i<=p[t];++i) ws[i]=1e9; ws[k]=0; double ans=0; for(int i=1;i<p[t];++i) { int ok=-1; for(int j=1;j<=p[t];++j) if(!v[j]&&(ok==-1||ws[j]<ws[ok])) ok=j; v[ok]=1; for(int j=1;j<=p[t];++j) if(d[q[t][ok]][q[t][j]]) ws[j]=min(ws[j],ws[ok]+d[q[t][ok]][q[t][j]]); } for(int i=1;i<=p[t];++i) { if(i==k) continue; ans=max(ans,ws[i]); } kuai[t][k]=ans; return ans; }//最短路算法 double liantong(int x,int y,int x1,int y1, double dd) { return max(max(kuai[x][x1]+kuai[y][y1]+dd,f[x]),f[y]); } int main() { //freopen("cowtour.in","r",stdin); //freopen("cowtour.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d%d",&x[i],&y[i]); char t; for(int i=1;i<=n;++i) { for(int j=1;j<=n;++j) { scanf(" %c",&t);//数据会有空格 int a=x[i]-x[j]; int b=y[i]-y[j]; d[i][j]=(t=='1'?sqrt(a*a+b*b):1e9); d[j][i]=d[i][j]; } } int k=0; for(int i=1;i<=n;++i) if(!belong[i]) DFS(i,++k);//DFS求出每个连通块 for(int i=1;i<=k;++i) { for(int j=1;j<=p[i];++j) f[i]=max(f[i],zhijing(i,j)); } double tr=1e9; for(int i=1;i<=k;++i) { for(int j=1;j<=k;++j) { if(i==j) continue; for(int a1=1;a1<=p[i];++a1) { for(int a2=1;a2<=p[j];++a2) { int a = q[i][a1], b = q[j][a2]; int dx = x[a]-x[b]; int dy = y[a]-y[b]; tr=min(tr,liantong(i,j,a1,a2,sqrt(dx*dx+dy*dy))); } } } } printf("%.6f\n",tr); return 0; }