题目链接: http://poj.org/problem?id=2728
题意: 在三维坐标系中,给定 N 个村庄, 要求在N个村庄之间连接水管,使其所有村庄相连(直接或间接),修水管的费用为z坐标之差, 路程为 两点在xoy面的投影距离, 要求 总花费 / 总路程 的比值最小。
裸的 最优比率生成树题目, 关于 最优比率生成树 http://www.cnblogs.com/lotus3x/archive/2009/03/21/1418480.html 这个博客,写的賊好。
此处AC代码用的 prim + 二分
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn = 1010;
const double inf = 0x3f3f3f3f;
const double eps = 1e-8;
int x[maxn],y[maxn],z[maxn];
double cost[maxn][maxn]; ///cost[i][j] 建造 i , j 村庄的花费
double dis[maxn][maxn]; ///dis[i][j] i , j 村庄的距离
int mst[maxn]; ///mst[i]判断点 i 是否属于最小生成树, == -1 则属于
double lowcost[maxn]; ///lowcost[i] 以某个点为起点 到 i 点的最小距离
int n;
double cal(int i,int j){
return sqrt(1.0*(x[i]-x[j])*(x[i]-x[j]) + 1.0*(y[i]-y[j])*(y[i]-y[j]));
}
double prim(int s,double rate){
for(int i = 1;i <= n;i ++){
mst[i] = s;
lowcost[i] = cost[s][i] - rate * dis[s][i];
}
mst[s] = -1;
int minid; double mi = inf;
double sum = 0;
for(int i = 1;i < n;i ++){
mi = inf; minid = -1;
for(int j = 2;j <= n;j ++){
if(lowcost[j] < mi && mst[j] != -1){
mi = lowcost[j];
minid = j;
}
}
if(minid != -1){
sum += mi;
mst[minid] = -1;
for(int j = 2;j <= n;j ++){
if(lowcost[j] > cost[minid][j] - rate * dis[minid][j] && mst[j] != -1){
lowcost[j] = cost[minid][j] - rate * dis[minid][j];
mst[j] = minid; ///此题中, 此句可要可不要,因为只是起标记作用
}
}
}
}
return sum;
}
int main()
{
while(~scanf("%d",&n)){
if(!n) break;
for(int i = 1;i <= n;i ++)
scanf("%d%d%d",&x[i],&y[i],&z[i]);
for(int i = 1;i <= n;i ++){
for(int j = 1;j <= n;j ++){
if(i == j) continue;
dis[i][j] = cal(i,j);
cost[i][j] = abs(z[i] - z[j]);
}
}
double l = 0.0,r = 100.0,mid;
while(r - l > eps){
mid = (l + r) / 2;
if(prim(1,mid) >= 0) l = mid;
else r = mid;
}
printf("%.3f\n",r);
}
return 0;
}