来源:POJ2728
按照题意,我们需要求一个(sum[1,n](h[i]))/(sum[1,n](d[i])最小的一颗生成树,那么这棵树到底可不可以呢?
我们采取二分的手段来获取我们的可行性
具体来说就是,我们假设我们有一个最小值,min,这个时候就有sum[1,n](h[i])>=min*sum[1,n](d[i]),移项合并同类项可以化简为:sum[1,n](h[i]-min*d[i])>=0
这样的式子就满足最小生成树的方法了
这一题使用prim比较优越,因为prim的权值计算是动态的
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int n;
const int MAXN = 1010;
const double eps = 1e-8;
const double MAXDOUBLE = 1.0*0x7fffffff;
struct Village{
int x,y;
int h;
Village(int x1=0,int y_=0,int z1=0):x(x1),y(y_),h(z1){}
void ini(int x1,int y_,int z1){
x=x1;
y=y_;
h=z1;
}
}v[MAXN];
double edge[MAXN][MAXN],ans;
double d[MAXN];
int next[MAXN];
double tran(Village a,Village b){
return sqrt((a.x-b.x)*(a.x-b.x)*1.0+(a.y-b.y)*(a.y-b.y)*1.0);
}
double prim(double mid){
double ans=0;
double len=0;
double sum=0;
for(int i=1;i<=n;i++){
next[i]=1;
d[i]=abs(v[i].h-v[1].h)-mid*edge[1][i];
}
next[1]=-1;
for(int i=1;i<n;i++){
double dm = MAXDOUBLE;
int dv=-1;
for(int j=1;j<=n;j++){
if(next[j]!=-1 && d[j]<dm){
dm=d[j];
dv=j;
}
}
if(dv!=-1){
ans+=abs(v[next[dv]].h-v[dv].h);
len+=edge[next[dv]][dv];
next[dv]=-1;
sum+=d[dv];
for(int j=1;j<=n;j++){
if(next[j]!=-1&&abs(v[dv].h-v[j].h)-mid*edge[dv][j]<d[j]){
d[j]=abs(v[dv].h-v[j].h)-mid*edge[dv][j];
next[j]=dv;
}
}
}
}
return sum;
}
bool judge(double mid){
if(prim(mid)>=0) return 1;
else return 0;
}
int main(){
while(scanf("%d",&n)!=EOF&&n){
int x,y,z;
for(int i=1;i<=n;i++){
scanf("%d%d%d",&v[i].x,&v[i].y,&v[i].h);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
edge[i][j]=tran(v[i],v[j]);
}
}
double l,r,mid;
l=0;
r=100.0;
while(r-l>eps){
mid = (l+r) /2;
if(judge(mid)) l=mid;
else r=mid;
}
printf("%.3f\n",l);
}
return 0;
}