点击打开链接
题意: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; }