思路:设sum(cost[i])/sum(dis[i])=r;那么要使r最小,也就是minsum(cost[i]-r*dis[i]);那么就以cost[i]-r*dis[i]为边权重新建边。当求和使得最小生成树的
sum(cost[i]-r*dis[i])==0时,这个r就是最优的。这个证明是01分数规划。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define Maxn 1010 #define Maxm Maxn*Maxn #define inf 1e16 #define eps 1e-6 using namespace std; int vi[Maxn],n; double dis[Maxn][Maxn],cost[Maxn][Maxn],benefit[Maxn][Maxn],far[Maxn]; struct Point{ double x,y,z; }p[Maxn]; void init() { memset(dis,0,sizeof(dis)); memset(vi,0,sizeof(vi)); memset(cost,0,sizeof(cost)); memset(far,0,sizeof(far)); } double Dis(Point a,Point b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } double prime(double r) { int i,j,temp; double ans,Max; memset(vi,0,sizeof(vi)); ans=0; for(i=2;i<=n;i++) far[i]=inf; far[1]=0; for(i=1;i<=n;i++) { Max=inf; for(j=1;j<=n;j++) { if(!vi[j]&&far[j]<Max) { Max=far[j]; temp=j; } } vi[temp]=1; ans+=Max; // dis[temp][j]-cost[temp][j] for(j=1;j<=n;j++) { if(!vi[j]&&far[j]>cost[temp][j]-r*dis[temp][j]) far[j]=cost[temp][j]-r*dis[temp][j]; } } return ans; } int main() { int i,j,a,b,c; double Max; while(scanf("%d",&n)!=EOF,n) { init(); Max=-inf; for(i=1;i<=n;i++) { scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].z); } for(i=1;i<n;i++) { for(j=i+1;j<=n;j++) { dis[j][i]=dis[i][j]=Dis(p[i],p[j]); Max=max(dis[i][j],Max); cost[i][j]=cost[j][i]=fabs(p[i].z-p[j].z); } } double l,r,mid; l=0,r=100000; double temp; while(r-l>eps) { mid=(l+r)/2; temp=prime(mid); if(temp>0) l=mid; else r=mid; } printf("%.3lf ",l); } return 0; }