题意:给出一个图,每个顶点有一个权值,要求求出一个生成树,这个树上的某一边长变为0,求该边两端点权值之和与总边权的最大比值。
思路:枚举权值为0的边,如过该边在最小生成树上,直接减去边权,如果不在树上,添加边必然产生环,此时最小总边权可以通过减去这个环上的最大边权求得。先用prim算法计算最小生成树,计算的时候已经在树上的点和正在添加的点之间路径上的最大边权就是添加的这条边或者和它连边的那个点对应的最大边权。求出所有最大边权之后枚举加边即可。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include<bits/stdc++.h> #define pb push_back #define se second #define fs first #define sq(x) (x)*(x) #define eps 0.0000000001 using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=1005; int T; int n; int po[maxv],X[maxv],Y[maxv]; double G[maxv][maxv]; double maxlen[maxv][maxv]; bool used[maxv]; bool eused[maxv][maxv]; double mincost[maxv]; int mincostfrom[maxv]; double prim(){ memset(used,0,sizeof used); memset(maxlen,0,sizeof maxlen); memset(eused,0,sizeof eused); for(int i=0;i<maxv;i++){ maxlen[i][i]=0; } double ans=0; mincostfrom[0]=0; for(int i=0;i<=n;i++) mincost[i]=1e20; mincost[0]=0; int added=0; while(added<n){ double cost=1e8; int from,to; for(int i=0;i<n;i++){ if(!used[i]&&mincost[i]<cost){ from=mincostfrom[i]; cost=mincost[i]; to=i; } } eused[from][to]=eused[to][from]=1; added++; ans+=cost; used[to]=1; for(int i=0;i<n;i++){ if(used[i]&&to!=i) maxlen[to][i]=maxlen[i][to]=max(maxlen[i][from],max(maxlen[to][i],cost)); } for(int i=0;i<n;i++){ if(!used[i]&&G[to][i]<mincost[i]){ mincostfrom[i]=to; mincost[i]=G[to][i]; } } } return ans; } int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>T; while(T--){ cin>>n; for(int i=0;i<n;i++){ scanf("%d%d%d",X+i,Y+i,po+i); } for(int i=0;i<n;i++){ for(int j=i+1;j<n;j++){ double dis=sqrt((double)sq(X[i]-X[j])+sq(Y[i]-Y[j])); G[i][j]=G[j][i]=dis; } } double mst=prim(); double ans=0; for(int i=0;i<n;i++){ for(int j=i+1;j<n;j++){ double popu=po[i]+po[j]; double tollen; if(eused[i][j]){ tollen=mst-G[i][j]; } else tollen=mst-maxlen[i][j]; ans=max(ans,popu/tollen); } } printf("%.2f\n",ans); } return 0; }