题意:
给出n个城市,每个城市有坐标(x[i],y[i])和人数p[i],秦始皇想用n-1条道路把这n个城市连接起来,要求任意两个城市能互相到达,并且长度和最小,此时有一个道士说他可以用魔法修路,让其中一条路不需要花费钱和劳力。一方面秦始皇想让总长度尽可能小,另一方面道士想尽可能让更多的人受益魔法道路—所以秦世皇决定A/B的值必须是最大的,A是由魔法道路连接的两个城市的总人口,B是非魔法道路的总长度。
题解:
对于任意一条边,我们可以知道它的A值肯定是固定的,所以我们需要让B值尽可能小,才能让A/B更大,所以我们要求的就是包含u->v这条边的最小生成树。
我们可以先求出原图的最小生成树,如果u->v这条边在最小生成树中,那么直接求比例即可;如果不在最小生成树中,那么当我们把这条边加入以后肯定会形成一个环,用mx[i][j]表示u点到v点路径上的边权的最大值,然后只需要删除环中的最大边权即可
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
#define LL long long
const int MAXN = 1000+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
int n,m,T,tot,pre[MAXN],vis[MAXN*MAXN/2],x[MAXN],y[MAXN],p[MAXN];
struct node{ int u,v; double w; }edge[MAXN*MAXN/2];
vector<int> g[MAXN]; double mx[MAXN][MAXN];
inline bool cmp(node x,node y){ return x.w<y.w; }
int Find(int x){ return pre[x]==x ? pre[x]:pre[x]=Find(pre[x]); }
inline double dis(int x,int y,int xx,int yy){
return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
inline double Kruskal(){
sort(edge+1,edge+tot+1,cmp);
int cnt=0; double res=0;
for(int i=1;i<=tot;i++){
int xx=Find(edge[i].u),yy=Find(edge[i].v); double w=edge[i].w;
if(xx!=yy){
cnt++; vis[i]=true; res+=w;
for(int j=0;j<g[xx].size();j++)
for(int k=0;k<g[yy].size();k++)
mx[g[xx][j]][g[yy][k]] = mx[g[yy][k]][g[xx][j]] = w;
pre[xx]=yy;
for(int j=0;j<g[xx].size();j++)
g[yy].push_back(g[xx][j]);
}
if(cnt==n-1) return res;
}
return res;
}
signed main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n); tot=0;
for(int i=1;i<=n;i++) g[i].clear(),g[i].push_back(i),pre[i]=i;
for(int i=1;i<=n;i++) scanf("%d%d%d",&x[i],&y[i],&p[i]);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
edge[++tot]=node{i,j,dis(x[i],y[i],x[j],y[j])},vis[tot]=false;
double MST=Kruskal(),res=0;
for(int i=1;i<=tot;i++){
int u=edge[i].u,v=edge[i].v; double w=edge[i].w;
if(!vis[i]) res = max(res,1.0*(p[u]+p[v])/(MST-mx[u][v]));
else res = max(res,1.0*(p[u]+p[v])/(MST-w));
}
printf("%.2f\n",res);
}
return 0;
}