题意:n个点用n-1条边连起来,问两个点的人口A除以总的距离减去这两点的距离的最大值,表达能力有限,看看别人怎么翻译的吧,但是代码还是不错的......
思路:用到的是次小生成树中的记录i->j的最大距离,然后枚举每条边,因为已经求出了最小生成树,所以如果你加入这条边后,肯定会形成一个环(画图会有奇效),然后我们减去这个环的最大值,就可以保证B最小,遍历所有边后,输出最大值,而且这个环的最大值就是最小生成树上的一条边,这是肯定的,之前这里没有理解,想了大半天
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=1010;
double num[maxn][maxn],dis[maxn],path[maxn][maxn];//path记录i->j的最大值
int X[maxn],Y[maxn],P[maxn],per[maxn],v[maxn],n;
bool vis[maxn][maxn];
double cal(int i,int j){
double t=(double)(X[i]-X[j])*(X[i]-X[j])+(double)(Y[i]-Y[j])*(Y[i]-Y[j]);
return sqrt(t);
}
double prim(){
memset(v,0,sizeof(v));
memset(vis,0,sizeof(vis));
memset(path,0,sizeof(path));
v[1]=1;
double ans=0;
for(int i=1;i<=n;i++){
dis[i]=num[1][i];
per[i]=1;
}
for(int i=1;i<=n;i++){
int minidx=0;
for(int j=1;j<=n;j++){
if(!v[j]){
if(minidx==0||dis[j]<dis[minidx]){
minidx=j;
}
}
}
vis[minidx][per[minidx]]=vis[per[minidx]][minidx]=1;//这条边是最小生成路的一条边,记录下来下面好判断
ans+=num[minidx][per[minidx]];
v[minidx]=1;
for(int j=1;j<=n;j++){
if(v[j]&&j!=minidx){
path[minidx][j]=path[j][minidx]=max(path[j][per[minidx]],dis[minidx]);
//因为最小生成树的prim算法跑得是点,kruskal跑得是边,所以我们每次更新这个点时,他不会是孤立的一个店或边,所以
//依次类推,这个边肯定是最大值,我感觉这种题多画图看看有助于理解
}
if(!v[j]){
if(dis[j]>num[minidx][j]){
dis[j]=num[minidx][j];
per[j]=minidx;
}
}
}
}
return ans;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
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=1;j<=n;j++){
num[i][j]=cal(i,j);
}
}
double ans=0;
double sum=prim();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j) continue;
if(vis[i][j]){
ans=max(ans,(P[i]+P[j])/(sum-num[i][j]));
}else ans=max(ans,(P[i]+P[j])/(sum-path[i][j]));
}
}
printf("%.2lf\n",ans);
}
return 0;
}